Migrate from ASP.NET Web API to ASP.NET Core

By Scott Addie and Steve Smith

An ASP.NET 4.x Web API is an HTTP service that reaches a broad range of clients, including browsers and mobile devices. ASP.NET Core unifies ASP.NET 4.x's MVC and Web API app models into a simpler programming model known as ASP.NET Core MVC. This article demonstrates the steps required to migrate from ASP.NET 4.x Web API to ASP.NET Core MVC.

View or download sample code (how to download)

Prerequisites

Review ASP.NET 4.x Web API project

As a starting point, this article uses the ProductsApp project created in Getting Started with ASP.NET Web API 2. In that project, a simple ASP.NET 4.x Web API project is configured as follows.

In Global.asax.cs, a call is made to WebApiConfig.Register:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Routing;

namespace ProductsApp
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
}

The WebApiConfig class is found in the App_Start folder and has a static Register method:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace ProductsApp
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

This class configures attribute routing, although it's not actually being used in the project. It also configures the routing table, which is used by ASP.NET Web API. In this case, ASP.NET 4.x Web API expects URLs to match the format /api/{controller}/{id}, with {id} being optional.

The ProductsApp project includes one controller. The controller inherits from ApiController and contains two actions:

using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {
        Product[] products = new Product[] 
        { 
            new Product
            {
                Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1
            }, 
            new Product
            {
                Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M
            }, 
            new Product
            {
                Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M
            } 
        };

        public IEnumerable<Product> GetAllProducts()
        {
            return products;
        }

        public IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
    }
}

The Product model used by ProductsController is a simple class:

namespace ProductsApp.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

The following sections demonstrate migration of the Web API project to ASP.NET Core MVC.

Create destination project

Complete the following steps in Visual Studio:

  • Go to File > New > Project > Other Project Types > Visual Studio Solutions. Select Blank Solution, and name the solution WebAPIMigration. Click the OK button.
  • Add the existing ProductsApp project to the solution.
  • Add a new ASP.NET Core Web Application project to the solution. Select the .NET Core target framework from the drop-down, and select the API project template. Name the project ProductsCore, and click the OK button.

The solution now contains two projects. The following sections explain migrating the ProductsApp project's contents to the ProductsCore project.

Migrate configuration

ASP.NET Core doesn't use the App_Start folder or the Global.asax file, and the web.config file is added at publish time. Startup.cs is the replacement for Global.asax and is located in the project root. The Startup class handles all app startup tasks. For more information, see App startup in ASP.NET Core.

In ASP.NET Core MVC, attribute routing is included by default when UseMvc is called in Startup.Configure. The following UseMvc call replaces the ProductsApp project's App_Start/WebApiConfig.cs file:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseMvc();
}

Migrate models and controllers

Copy over the ProductApp project's controller and the model it uses. Follow these steps:

  1. Copy Controllers/ProductsController.cs from the original project to the new one.
  2. Copy the entire Models folder from the original project to the new one.
  3. Change the copied files' namespaces to match the new project name (ProductsCore). Adjust the using ProductsApp.Models; statement in ProductsController.cs too.

At this point, building the app results in a number of compilation errors. The errors occur because the following components don't exist in ASP.NET Core:

  • ApiController class
  • System.Web.Http namespace
  • IHttpActionResult interface

Fix the errors as follows:

  1. Change ApiController to ControllerBase. Add using Microsoft.AspNetCore.Mvc; to resolve the ControllerBase reference.
  2. Delete using System.Web.Http;.
  3. Change the GetProduct action's return type from IHttpActionResult to ActionResult<Product>.

Simplify the GetProduct action's return statement to the following:

return product;

Configure routing

Configure routing as follows:

  1. Decorate the ProductsController class with the following attributes:

    [Route("api/[controller]")]
    [ApiController]
    

    The preceding [Route] attribute configures the controller's attribute routing pattern. The [ApiController] attribute makes attribute routing a requirement for all actions in this controller.

    Attribute routing supports tokens, such as [controller] and [action]. At runtime, each token is replaced with the name of the controller or action, respectively, to which the attribute has been applied. The tokens reduce the number of magic strings in the project. The tokens also ensure routes remain synchronized with the corresponding controllers and actions when automatic rename refactorings are applied.

  2. Set the project's compatibility mode to ASP.NET Core 2.2:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }
    

    The preceding change:

    • Is required to use the [ApiController] attribute at the controller level.
    • Opts in to potentially breaking behaviors introduced in ASP.NET Core 2.2.
  3. Enable HTTP Get requests to the ProductController actions:

    • Apply the [HttpGet] attribute to the GetAllProducts action.
    • Apply the [HttpGet("{id}")] attribute to the GetProduct action.

After the preceding changes and the removal of unused using statements, ProductsController.cs file looks like this:

using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using ProductsCore.Models;

namespace ProductsCore.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        Product[] products = new Product[] 
        { 
            new Product
            {
                Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1
            }, 
            new Product
            {
                Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M
            }, 
            new Product
            {
                Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M
            }
        };

        [HttpGet]
        public IEnumerable<Product> GetAllProducts()
        {
            return products;
        }

        [HttpGet("{id}")]
        public ActionResult<Product> GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return product;
        }
    }
}

Run the migrated project, and browse to /api/products. A full list of three products appears. Browse to /api/products/1. The first product appears.

Compatibility shim

The Microsoft.AspNetCore.Mvc.WebApiCompatShim library provides a compatibility shim to move ASP.NET 4.x Web API projects to ASP.NET Core. The compatibility shim extends ASP.NET Core to support a number of conventions from ASP.NET 4.x Web API 2. The sample ported previously in this document is basic enough that the compatibility shim was unnecessary. For larger projects, using the compatibility shim can be useful for temporarily bridging the API gap between ASP.NET Core and ASP.NET 4.x Web API 2.

The Web API compatibility shim is meant to be used as a temporary measure to support migrating large ASP.NET 4.x Web API projects to ASP.NET Core. Over time, projects should be updated to use ASP.NET Core patterns instead of relying on the compatibility shim.

Compatibility features included in Microsoft.AspNetCore.Mvc.WebApiCompatShim include:

  • Adds an ApiController type so that controllers' base types don't need to be updated.
  • Enables Web API-style model binding. ASP.NET Core MVC model binding functions similarly to that of ASP.NET 4.x MVC 5, by default. The compatibility shim changes model binding to be more similar to ASP.NET 4.x Web API 2 model binding conventions. For example, complex types are automatically bound from the request body.
  • Extends model binding so that controller actions can take parameters of type HttpRequestMessage.
  • Adds message formatters allowing actions to return results of type HttpResponseMessage.
  • Adds additional response methods that Web API 2 actions may have used to serve responses:
    • HttpResponseMessage generators:
      • CreateResponse<T>
      • CreateErrorResponse
    • Action result methods:
      • BadRequestErrorMessageResult
      • ExceptionResult
      • InternalServerErrorResult
      • InvalidModelStateResult
      • NegotiatedContentResult
      • ResponseMessageResult
  • Adds an instance of IContentNegotiator to the app's dependency injection (DI) container and makes available the content negotiation-related types from Microsoft.AspNet.WebApi.Client. Examples of such types include DefaultContentNegotiator and MediaTypeFormatter.

To use the compatibility shim:

  1. Install the Microsoft.AspNetCore.Mvc.WebApiCompatShim NuGet package.
  2. Register the compatibility shim's services with the app's DI container by calling services.AddMvc().AddWebApiConventions() in Startup.ConfigureServices.
  3. Define web API-specific routes using MapWebApiRoute on the IRouteBuilder in the app's IApplicationBuilder.UseMvc call.

Additional resources