Writing APIs in ASP.NET Core 2.1 with the ApiController Attribute


Writing Apis in ASP.NET Core until version 2.0 for me has been a great experience. I love the fact that we can now just use the same framework for both web applications and APIs.

It's great to use the same Controller base class but fact remains that a Controller for an API has different needs in terms of functionality than a Controller for an MVC application that works with views.

The ApiController attribute adds some great functionality to ASP.NET Core 2.1 controllers that makes life easier for API developers.

Attribute routing

The ApiController attribute offers a couple of very nice advantages but first a heads-up. When using an api controller you have to use attribute routing you can't use a routing table that you configure in the startup class.

Derive from ControllerBase

A new convention from now on is that we derive our Controller not from the Controller base class but from ControllerBase. This is a more light-weight controller that doesn't have support for everything related to views. Together with the ApiController attribute a ControllerBase derived class makes a beautiful API controller.

Auto model validation

When the Controller is decorated with Apicontroller, when a post or a put request comes in for your controller with data in the body that has to be bound to an object like in for example an OrderModel, the validation attributes of have to be checked. When you decorate your controller with ApiController that happens automatically now. When there is a problem the controller with automatically return a http status code 400, bad request that contains the problem. No need to write any code like this anymore over and over again:

if (!ModelState.IsValid) { return BadRequest(ModelState); }

Binding source inference

Without the APIController, If a method in your controller accepts a parameter, the controller will need instructions from you where to get the value from.

For example here in a controller I have a conferenceId parameter in this GetAll method.

[HttpGet("{conferenceId}")] public IActionResult GetAll(int conferenceId)

In this case the value comes from the routing. So when a GET request comes in the value has to be supplied as part of the URL. So for example /Proposal/1 where 1 is the conferenceId.

You can also instruct the controller to get the value from the body of the request if it is a post method with an attribute.

[HttpPost] public IActionResult Add([FromBody]ProposalModel model)

So with this add method for example I can use FromBody on the ProposalModel parameter.

There are a couple of attributes like [FromBody]:

  • [FromForm] looks for data as a result of an HTML form post.
  • [FromRoute] gets in from the route just like putting the parameter name in the HTTPGet attribute
  • [FromQuery] looks for it in the query string.
  • [FromHeader] gets it from an HTTP header
  • [FromServices] will ask the dependency injection container for a value.

When you use the ApiController attribute the source of the value is inferred, so in many cases you don't have to specify a binding attribute anymore. When the parameter is a complex type it will look in the body, when there's a form post it looks in the form data, when it's in the route template it will get it from the route and for all other values it will try the query string.

Tweet Post Share Update Email RSS