Enabling [ApiController] globally in ASP.NET Core 2.2

Β· 532 words Β· 3 minutes to read

A while ago we looked at how ApiControllerAttribute can be used in ASP.NET Core MVC to make Web API authoring a little bit more developer friendly.

Today let’s have a look at a tiny little improvement in ASP.NET Core 2.2 - the ability to set it up globally (and perhaps a few other things).

Global set up of [ApiController] πŸ”—

As a quick reminder, ApiControllerAttribute enables a couple of MVC behaviors/features which are geared towards HTTP API development. AMong those are, for example, automatic model state validation or automatic injection of the [FromBody] attribute onto the action parameters.

The normal usage of the attribute is to place it on the controller as shown on the next snippet.

[ApiController]
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost]
    public ActionResult<Book> PostBook(Book book)
    {
        // omitted from brevity
    }
}

Now, that’s all fine, but what happens if you’d like to avoid having to put it all over the place on every controller and have it globally enabled for all of the controllers? Well, the only reasonable solution in ASP.NET Core 2.1 was to put it on a base class and make all your controllers inherit from that class.

ASP.NET Core 2.2 comes with a little improvement in that area. Turns out that, in a rather unusual way for something that’s part of an MVC configuration, you could actually register [ApiController] at an assembly (!) level.

This is done as a typical assembly attribute and can be placed anywhere in an assembly. For example:

[assembly: ApiController]
namespace MyNamespace
{
    public class Startup
    {
        //omitted for brevity
    }
} 

The above setup will automatically propagate the [ApiController] feature to all controllers discovered in this assembly.

Unfortunately, there is still no way to dynamically register the attribute, for example using IApplicationModelConvetion or some sort of a filter - it must be done statically.

ApiBehaviorOptions customization improvements πŸ”—

One additional improvement worth mentioning, that also shipped in ASP.NET Core 2.2, is a small change around customization of ApiBehaviorOptions (so really the customization of the [ApiController] feature behavior).

Previously you had to do it by using the usual Configure() API against the DI container. This work well for the most part, but created some rather unfortunate subtle bugs (and resulted in at least a couple open bugs in the MVC repo), because suddenly the order of calls mattered. More specifically, the Configure() had to be called after the call to AddMvc() was done, otherwise AddMvc() would overwrite our customizations.

In ASP.NET Core MVC 2.2 customization of ApiBehaviorOptions can be done via an extension method on IMvcBuilder or IMvcCoreBuilder - and this is actually the recommended approach going forward. This is shown next:

// ASP.NET Core MVC 2.1
services.AddMvc();
services.Configure<ApiBehaviorOptions>(o =>
{
    o.InvalidModelStateResponseFactory = context =>
    {
        var error = new
        {
            Detail = "Custom error"
        };

        return new BadRequestObjectResult(error);
    };
};

// ASP.NET Core MVC 2.2
services.AddMvc().ConfigureApiBehaviorOptions(o =>
{
    o.InvalidModelStateResponseFactory = context =>
    {
        var error = new
        {
            Detail = "Custom error"
        };

        return new BadRequestObjectResult(error);
    };
});

This way you will not have to worry anymore about adding the Configure() in the correct order, as you’d be simply configuring the API Controller features directly off the IMvcBuilder or IMvcCoreBuilder.

About


Hi! I'm Filip W., a software architect from ZΓΌrich πŸ‡¨πŸ‡­. I like Toronto Maple Leafs πŸ‡¨πŸ‡¦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁒󠁳󠁣󠁴󠁿.

You can find me on Github, on Mastodon and on Bluesky.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP