How to implement a "pure" ASP.NET Core Web API by using AddMvcCore()
Asked Answered
T

1

40

I've seen a lot of ASP.NET Core Web API projects that use the default AddMvc() service without the realizing that using AddMvcCore() is a superior option due to the control over services.

How exactly do you implement an ASP.NET Core Web API by using AddMvcCore() and why is it better?

Thermoelectric answered 21/2, 2017 at 10:50 Comment(0)
T
90

What is the difference between AddMvc() and AddMvcCore()?

The first thing key thing to understand is that AddMvc() is just a pre-loaded version of AddMvcCore(). You can see the exact implementation of the AddMvc() extension at the GitHub repository.

I like using default VS templates as much as the next guy, but sometimes you need to know when it's the wrong choice. I have seen several guides online that lean more towards an attempt to "undo" these default services rather than just going with a solution that just does not implement them in the first place.

With the advent of ASP.NET Core being open source, there really isn't a good reason why we can't peel back a layer and work at a lower level without fear of losing "magic".


Definition of "minimal" and "pure"

Note: The definitions are intended for the context of this answer only. Mostly for the sake of clarity and assisting in further understanding.

This answer leans more towards "pure" and not "minimal". I'd like to describe why, so it's clearer what I'm talking about.

Minimal. A "minimal" solution would be an implementation that does not even call upon the AddMvcCore() method at all. The reason for this, is that MVC is not really a "required" component to assembling you own Web API, and it certainly adds some weight to your code with the additional dependencies. In this scenario, since you're not using the AddMvcCore() method, you also would not inject it into your application, here

public void Configure(IApplicationBuilder app)
{
    app.UseMvc(); // you don't need this
}

This would mean mapping your own routes and responding to the context in your own way. This really isn't challenging at all, but I don't want to dive into it, because it's quite off-topic, but here is a tiny taste of a minimal implementation:

public void Configure(IApplicationBuilder app)
{
    app.Map("/api", HandleMapApi);
    // notice how we don't have app.UseMvc()?
}    

private static void HandleMapApi(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        // implement your own response
        await context.Response.WriteAsync("Hello WebAPI!");
    });
}

For many projects, a "minimal" approach means we are giving up some of the features found in MVC. You would really have to weigh your options and see if you this design path is the right choice, as there is a balance between design pattern, convenience, maintainability, code footprint, and most importantly performance and latency. Simply put: a "minimal" solution would mean minimizing the services and middleware between your code and the request.

Pure. A "pure" solution (as far as the context of this answer) is to avoid all the default services and middleware that come "pre-bundled" with AddMvc() by not implementing it in the first place. Instead, we use AddMvcCore(), which is explained further in the next section:


Implementing our own services / middleware with AddMvcCore()

The first thing to get started is to setup ConfigureServices to using AddMvcCore(). If you look at the GitHub repository, you can see that AddMvc() calls AddMvcCore() with a standard set of services / middleware:

Here are some of the services / middleware that stands out as "unneeded":

var builder = services.AddMvcCore();

builder.AddViews();
builder.AddRazorViewEngine();
builder.AddRazorPages();

Many of these default services are great for a general web project, but are usually undesirable for a "pure" Web API.

Here is a sample implementation of ConfigureServices using AddMvcCore() for a Web API:

public void ConfigureServices(IServiceCollection services)
{
    // Build a customized MVC implementation, without using the default AddMvc(),
    // instead use AddMvcCore(). The repository link is below:
    // https://github.com/aspnet/Mvc/blob/release/2.2/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs

    services
        .AddMvcCore(options =>
        {
            options.RequireHttpsPermanent = true; // this does not affect api requests
            options.RespectBrowserAcceptHeader = true; // false by default
            //options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();

            // these two are here to show you where to include custom formatters
            options.OutputFormatters.Add(new CustomOutputFormatter());
            options.InputFormatters.Add(new CustomInputFormatter());
        })
        //.AddApiExplorer()
        //.AddAuthorization()
        .AddFormatterMappings()
        //.AddCacheTagHelper()
        //.AddDataAnnotations()
        //.AddCors()
        .AddJsonFormatters();
}

The implementation above is mostly a duplicate of the AddMvc() extension method, however I have added a few new areas so that others can see the added benefits of doing this.

  • Custom Input/Output Formatters. This is where you can do your own highly optimized serializers (such as Protobuf, Thrift, Avro, Etc) instead of using JSON (or worse XML) serialization.
  • Request Header Handling. You can make sure that the Accept header is recognized, or not.
  • Authorization Handling. You can implement your own custom authorization or can take advantage of the built-in features.
  • ApiExplorer. For some projects, you may likely include it, otherwise some WebAPI's may not want to this feature.
  • Cross-Origin Requests (CORS). If you need a more relaxed security on your WebAPI, you could enable it.

Hopefully with this example of a "pure" solution, you can see the benefits of using AddMvcCore() and be comfortable with using it.

If you're serious about control over performance and latency while working on top of ASP.NET Core's web host maybe a deep dive into a "minimal" solution is where you're dealing right at the edge of the request pipeline, rather than letting it get bogged down by the MVC middleware.


Additional Reading

A visual look at how the middleware pipeline looks like... As per my definitions, less layers means "minimal", whereas "pure" is just a clean version of MVC.

enter image description here

You can read more about it on the Microsoft Documents: ASP.NET Core Middleware Fundamentals

Thermoelectric answered 21/2, 2017 at 10:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.