Migrating from ASP.NET Web API to MVC 6 – exploring Web API Compatibility Shim

Migrating an MVC 5 project to ASP.NET 5 and MVC 6 is a big challenge given that both of the latter are complete rewrites of their predecessors. As a result, even if on the surface things seem similar (we have controllers, filters, actions etc), as you go deeper under the hood you realize that most, if not all, of your pipeline customizations will be incompatible with the new framework.

This pain is even more amplified if you try to migrate Web API 2 project to MVC 6 – because Web API had a bunch of its own unique concepts and specialized classes, all of which only complicate the migration attempts.

ASP.NET team provides an extra convention set on top of MVC 6, called “Web API Compatibility Shim”, which can be enabled make the process of migration from Web API 2 a bit easier. Let’s explore what’s in there.

Introduction

If you create a new MVC 6 project from the default starter template, it will contain the following code in the Startup class, under ConfigureServices method:

This pretty much explains it all – the Compatibility Shim is included in an external package, Microsoft.AspNet.Mvc.WebApiCompatShim and by default is switched off for new MVC projects. Once added and enabled, you can also have a look at the UseMvc method, under Configure. This is where central Web API routes can be defined:

Interestingly, Web API Compatibility Shim will store all Web API related routes under a dedicated, predefined area, called simply “api”.

This is all that’s needed to get you started.

Inheriting from ApiController

Since the base class for Web API controllers was not Controller but ApiController, the shim introduces a type of the same name into MVC 6.

While it is obviously not 100% identical to the ApiController from Web API, it contains the majority of public proeprties and methods that you might have gotten used to – the Request property, the User property or a bunch of IHttpActionResult helpers.

You explore it in details here on Github.

Returning HttpResponseMessage

The shim introduces the ability to work with HttpResponseMessage in MVC 6 projects. How is this achieved? First of all, the Microsoft.AspNet.WebApi.Client package is referenced, and that brings in the familiar types – HttpResponseMessage and HttpRequestMessage.

On top of that, an extra formatter (remember, we discussed MVC 6 formatters here before) is injected into your application – HttpResponseMessageOutputFormatter. This allows you to return HttpResponseMessage from your actions, just like you were used to doing in Web API projects!

How does it work under the hood? Remember, in Web API, returning an instance of HttpResponseMessage bypassed content negotiation and simply forwarded the instance all the way to the hosting layer, which was responsible to convert it to a response that was relevant for a given host (HttpResponse under System.Web, OWIN dictionary under OWIN and so on).

In the case of MVC 6, the new formatter will grab your HttpResponseMessage and copy its headers and contents onto the Microsoft.AspNet.Http.HttpResponse which is the new abstraction for HTTP response in ASP.NET 5.

As a result such type of an action as the one shown below, is possible in MVC 6, and as a consequence it should be much simpler to migrate your Web API 2 projects.

Binding HttpRequestMessage

In Web API it was possible to bind HttpRequestMessage in your actions. For example this was easily doable:

The shim introduces an HttpRequestMessageModelBinder which allows the same thing to be done under MVC 6. As a result, if you relied on HttpRequestMessage binding in Web API, your code will migrate to MVC 6 fine.

How does it work? The shim will use an intermediary type, HttpRequestMessageFeature, to create an instance of HttpRequestMessage from the ASP.NET 5 HttpContext.

HttpRequestMessage extensions

Since it was very common in the Web API world to use HttpResponseMessage as an action return type, there was a need for a mechanism that allowed easy creation of its instances. This was typically achieved by using the extension methods on the HttpRequestMessage, as they would perform content negotiation for you.

For example:

The Web API Compatibility Shim introduces these extension methods into MVC 6 as well. Mind you, these extension methods are hanging off the HttpRequestMessage – so to use them you will have to inherit from the ApiController, in order to have the Request property available for you on the base class in the first place.

There is a number of the overloaded versions of those CreateResponse method (just like it was in Web API), as well as the CreateErrorResponse method. You can explore them in detail here on Github.

HttpError

If you use/used the CreateErrorResponse method mentioned above, you will end up relying on the HttpError class which is another ghost of the Web API past rejuvenated by the compatibility shim.

HttpError was traditionally used by Web API to serve up error information to the client in a (kind of) standardized way. It contained properties such as ModelState, MessageDetail or StackTrace.

It was used by not just the CreateErrorResponse extension method but also by a bunch of IHttpActionResultsInvalidModelStateResult, ExceptionResult and BadRequestErrorMessageResult. As a result, HttpError is back to facilitate all of these types.

More Web API-style parameter binding

A while ago, Badrinarayanan had a nice post explaining the differences between parameter binding in MVC 6 and Web API 2. In short, the approach in MVC 6 is (and that’s unerstandable) much more like MVC 5, and those of us used to Web API style of binding, could easily end up with lots of problems (mainly hidden problems that wouldn’t show up at compile time, which is even worse). Even the official tutorials from Microsoft show that, for example, binding from the body of the request, which was one of the cornerstones of Web API parameter binding, needs to be explicit now (see the action CreateTodoItem in that example) – through a use of a dedicated attribute.

Web API Compatibility Shim attempts to close this wide gap between MVC 6 and Web API 2 style of parameter binding.

The class called WebApiParameterConventionsApplicationModelConvention introduces the model of binding familiar to the Web API developers:

  • – simple types are bound from URI
  • – complex types are bound from the body

Additionally action overloading was different between MVC and Web API (for example, Web API parameters are all required by default, whereas MVC ones aren’t). This action resolving mechanism is also taken care of by the compatibility shim.

Finally, FromUriAttribute, does not exist in MVC 6 (due to the nature of parameter binding in MVC). This type is, however, introduced again in the compatibility shim.

Support for HttpResponseException

In Web API, it was common to throw HttpResponseException to control the flow of your action and short circuit a relevant HTTP response to the client. For example:

HttpResponseException is re-introduced into MVC 6 by the compatibility shim, and allows you to use the code shown above in the new world.

How does it work? Well, the shim introduces and extra filter, HttpResponseExceptionActionFilter that catches all exceptions from your action. If the exception happens to be of type HttpResponseException, it will be marked as handled and Result on the ActionExecutedContext will be properly set as ObjectResult, based on the contents of that exception. If the exception is of different type, the filter just lets it pass through to be handled elsewhere.

Support for DefaultContentNegotiator

Web API content negotiation has been discussed quite widely on this blog before. Compatibility Shim introduces the concept of the IContentNegotiator into MVC 6 to facilitate its own extension methods, such the already discussed CreateResponse or CreateErrorResponse.

Additionally, the IContentNegotiator is registered as a service so you can obtain an instance of it using a simple call off the new ASP.NET 5 HttpContext:

This makes it relatively easy to port pieces of code where you’d deal with the negotiator manually (such as the example below), as you’d only need to change the way the negotiator instance is obtained.

Extra action results

Web API Compatibility Shims also introduces a set of action results that you might be used to from Web API as IHttpActionResults. Those are currently implemented as ObjectResults.

Here is the list:
BadRequestErrorMessageResult
ConflictResult
ExceptionResult
InternalServerErrorResult
InvalidModelStateResult
NegotiatedContentResult
OkNegotiatedContentResult
OkResult
ResponseMessageResult

Regardless of how you used them in your Web API project – newed up directly in the action, or as a call to a method on the base ApiController, your code should now be much more straight forward to port to MVC 6 (or at least to get it to a stage that it actually compiles under MVC 6).

Summary

While the Web API Compatibility Shim will not solve all of the problems you might encounter when trying to port a Web API 2 project to MVC 6, it will at least make your life much easier. The majority of the high level concepts – the code used in your actions and controllers – should migrate almost 1:1, and the only things you will be left with having to tackle would be the deep pipeline customizations, so things such as custom routing conventions, customizations to action or controller selectors, customizations to tracers or error handlers and so on.

Either way, enabling the shim is a must to even get started with thinking to migrate a Web API project to MVC 6.

Be Sociable, Share!

  • Dave Van den Eynde

    And I was expecting a nice article on what the new paradigms for these use cases should be. I’d rather break hard instead of introducing a shim that might not work as expected.

    • Calvin

      At least you have an option if you just want a quick upgrade/conversion of existing projects. Nothing would stop you from going with pure MVC 6. This shim will be useful for many people.

  • Cecil L. Phillip

    This is awesome. What about registering handlers? Is that all getting moved to middleware

    • http://www.strathweb.com/ Filip W

      middleware or a global filter, but have to do everything by hand – no shim/helper classes on the horizon for them.

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1783()

  • Pingback: Dew Drop – January 22, 2015 (#1938) | Morning Dew()

  • http://www.Marisic.Net/ dotnetchris

    I’m glad Microsoft is acknowledging that WebApi was the wrong direction and instead focused entirely on MVC, but leveraging what they learned with WebApi to further enhance MVC. Your article here is the key difference between Microsoft and Google and why developer should always stick with Microsoft. Microsoft actually cares about us and real world problems. They could have just walked away from WebApi and told people you need to convert to MVC6 if you want new stuff, instead they provide a 80%-90% of the way painfree path to upgrade.

  • Pingback: Les liens de la semaine – Édition #116 | French Coding()

  • Livesoftware

    What about IHttpActionResult?

  • Pingback: Links of the month (January Edition) | Jan @ Development()

  • http://blog.deltacode.be/ David De Sloovere

    Hi Filip, thanks for another great post.
    Do you have any posts planned on migration of Web Api 2 to MVC 6? For those that don’t want to use the shim but go cold turkey.

  • Phil Bolduc

    We are about to start a project using WebApi. Due to the tight timelines, we cannot use MVC 6 yet. Are there any general guidelines of things to avoid minimize migration issues in the future? This post does introduce some of the issues. I will look at the implementation of the Web API Compatibility Shim to identify some of the pain points. We will try to stay clear of “deep pipeline customizations, so things such as custom routing conventions, customizations to action or controller selectors, customizations to tracers or error handlers” as you mentioned.

  • Jared Thirsk

    4 months later, and I am looking for how to use the MVC 6 style rather than this Shim. Any guides out there?

  • rickygi

    I’m currently exploring an approach for building an aspnet-5 mvc-6 API that complies with the JSON API 1.0 specification (http://jsonapi.org/). Would you consider the Web API Compatibility Shim as a reasonable guide for implementing a solution? For example, in the Startup class under the ConfigureServices method, I would like to use something similar to: services.AddJsonApiConventions();

  • Pingback: Main changes between MVC 5 and MVC 6()

  • Antonis

    What about namespaces parameter in routes.MapRoute above. This was removed in MVC6?