Automatic Model Validation and Parameter Binding using ApiController
Attribute
While developing ASP.NET Core Web API you need to perform model validation
manually. You also need to bind action parameters explicitly using attributes
such as [FromBody]. The [ApiController] attribute introduced in ASP.NET Core 2.1
can automate both of these tasks for you. This article explains how.
Consider the following Post() action from a Web API controller -
CustomerController.
[Route("api/[controller]")]
public class CustomerController : Controller
{
[HttpPost]
public IActionResult Post([FromBody]Customer obj)
{
if(ModelState.IsValid)
{
//insert customer
return Ok();
}
else
{
return BadRequest();
}
}
}
The CustomerController contains just a single action - Post() - that is
supposed to insert a new customer in the database. Notice a couple of things
about this action:
- The action checks the ModelState.IsValid manually. And depending on the
outcome either returns 200 - Ok or 400 - Bad request as its response.
- The Post() action takes one parameter - obj - that is bound with the
incoming request body using [FromBody] attribute. In the absence of [FromBody]
obj will be null even though the client sends data as a part of request
body.
The Customer model class involved in the Web API is shown below:
public class Customer
{
[Required]
[StringLength(5)]
public string ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
}
Notice the use of validation attributes such as [Required] and [StringLength]
to validate Customer ID and Name properties.
Wouldn't it be nice if these simple yet common tasks are taken care of by the
framework itself? That's what [ApiController] attribute does.
Let's see how to use it. Have a look at the following code:
[Route("api/[controller]")]
[ApiController]
public class CustomerController : Controller
{
[HttpPost]
public IActionResult Post(Customer obj)
{
//insert customer
return Ok();
}
}
Notice that the CustomerController is now decorated with [ApiController]
attribute. The Post() action no longer has the model validation code. Moreover,
the obj parameter is not explicitly bound with the request body using [FromBody]
attribute. All these aspect are automatically taken care by the [ApiController]
attribute.
Since [ApiController] is introduced in ASP.NET Core 2.1 you also need to set
the compatibility version in the Startup class as shown below:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
The SetCompatibilityVersion() method sets the compatibility to version 2.1.
This line of code is required for [ApiController] to work as expected. So, make
sure to add in your application startup as explained above.
Now that we have [ApiController] in place, let's test whether it works as
expected. The following jQuery code makes a POST request to the
CustomerController.
$(document).ready(function () {
var options = {};
options.url = "/api/customer";
options.type = "POST";
options.contentType = "application/json";
var obj = {};
obj.ID = "AFLKI";
obj.Name = "Company Name 1";
options.data = JSON.stringify(obj);
options.dataType = "json";
options.success = function () {
console.log("Success!");
};
options.error = function (a,b,c) {
console.log(a);
console.log(b);
console.log(c);
};
$.ajax(options);
});
The above code creates a new Customer object (with ID and Name properties)
and sets the data property. The $.ajax() initiates the POST request to the Web
API.
Set a breakpoint in the Post() action and run the application.
If you pass valid values for ID and Name properties, the POST request will
reach the Post() action breakpoint. Moreover, the obj parameter will have
correct ID and Name values even if you didn't use [FromBody] attribute.
Now, set some invalid value for ID property, say - ALFKI123 (more than 5
characters) and run the application again. This time the framework will detect
that ModelState is invalid and will respond with 400 - Bad request to the
browser. This automatic model state checking happens due to [ApiController]
attribute. Since the framework is detecting the error before reaching the the
Post() action, the breakpoint will not be reached.
The following figure shows how the browser is sent a 400 - Bad Request
message.
Notice how the validation error message from the validation attributes is
available to the client via responseText property in addition to status and
statusText properties.
That's for now! Keep coding!!