This is another post relating to the new features in WebApi2.
When you start a new WebAPI controller, the template used is
public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // POST api/<controller> public void Post([FromBody]string value) { } // PUT api/<controller>/5 public void Put(int id, [FromBody]string value) { }
Once you start working with the WebAPI, you soon realize that this is not enough, as you need to return status information as well, so eventually you progress to using HttpResponseMessage as the return type for all calls. When using HttpResponseMessage, to send the response from an API, you typically use one of two methods:
- Request.CreateResponse()
- Request.CreateErrorResponse()
With each method, you can pass in an HttpStatusCode to get more fine grained control of what status code to be returned. There is a third method, but its only used for OData.
With WebAPI2, IHttpActionResult interface has been introduced. The definition from MSDN is “Defines a command that asynchronously creates an HttpResponseMessage.”
It is now recommended to use IHttpActionResult instead of HttpResponseMessage for all WebAPI responses. To help with this, there are several common Methods to create responses:
- Ok()
- Ok<T>(T)
- BadRequest()
- NotFound()
A full list can be found here:
With the following method,
[ResponseType(typeof(Policy))] public HttpResponseMessage Get(int id) { var policyToReturn = _policyRepository.GetPolicyFor(id); if ( policyToReturn == null ) return Request.CreateResponse(HttpStatusCode.NotFound); var response = Request.CreateResponse(HttpStatusCode.OK, policyToReturn); return response; }
Using IHttpActionResult, is the Get method is now
[ResponseType(typeof(Policy))] public IHttpActionResult Get(int id) { var policyToReturn = _policyRepository.GetPolicyFor(id); if (policyToReturn == null) return NotFound(); return Ok(policyToReturn); }
Just comparing the before and after result of the change, the code looks cleaner. However, after changing the code, the test project will no longer compile. The following test will not compile
[Test] public void When_Call_Get_Then_Expected_Data_Is_Returned() { IList<Policy> mockDatabase = GetMockDatabase(); _mockPolicyRepository.Setup(m => m.Get()) .Returns(mockDatabase); var controller = GetControllerForTests(); var response = controller.Get(); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); var returnedObjects = response.Content. ReadAsStringAsync().Result; var returnedListOfPolicy = (List<Policy>)JsonConvert. DeserializeObject(returnedObjects, typeof(List<Policy>)); Assert.That(returnedListOfPolicy.Count, Is.EqualTo(mockDatabase.ToList().Count)); }
With the following errors:
- ‘System.Web.Http.IHttpActionResult’ does not contain a definition for ‘StatusCode’ and no extension method ‘StatusCode’ accepting a first argument of type ‘System.Web.Http.IHttpActionResult’ could be found (are you missing a using directive or an assembly reference?) C:\MyProjects\MotorDB\MotorDB.UI.Tests\ApiControllerTests\PolicyControllerTests.cs
- ‘System.Web.Http.IHttpActionResult’ does not contain a definition for ‘Content’ and no extension method ‘Content’ accepting a first argument of type ‘System.Web.Http.IHttpActionResult’ could be found (are you missing a using directive or an assembly reference?) C:\MyProjects\MotorDB\MotorDB.UI.Tests\ApiControllerTests\PolicyControllerTests.cs
As with clearning up the controller code, unit testing is also made easier. The above test is now
[Test] public void When_Call_Get_Then_Expected_Data_Is_Returned() { IList<Policy> mockDatabase = GetMockDatabase(); _mockPolicyRepository.Setup(m => m.Get()) .Returns(mockDatabase); var controller = GetControllerForTests(); var response = controller.Get(); var returnedListOfPolicy = response as OkNegotiatedContentResult<IList<Policy>>; Assert.That(returnedListOfPolicy, Is.Not.Null); Assert.That(returnedListOfPolicy.Content.Count, Is.EqualTo(mockDatabase.Count())); }
No more conversion of data, The OkNegotiatedContentResult does that for us.
The only thing that have not yet discovered what to do is to check the StatusCode like used to be able to. If you debug the unit test, and stop after the Get method, and inspect the response object, there is nothing to indicate the StatusCode of Get methods. Not yet worked on PUT or POST methods, those will be the topic of another post
Status is implicit. Since you use OkNegotiatedContentResult and check that it isn’t null, you know that the status is an OK status (200)