Web API迁移到ASP.NET Core

将从 ASP.NET Web API 迁移到 ASP.NET Core

作者: Scott AddieSteve Smith

ASP.NET 4.x Web API 是一种 HTTP 服务,它可达到各种客户端,包括浏览器和移动设备。 ASP.NET Core 将 ASP.NET 4.x 的 MVC 和 Web API 应用模型统一到称为 ASP.NET Core MVC 的更简单的编程模型中。 本文演示从 ASP.NET 4.x Web API 迁移到 ASP.NET Core MVC 所需的步骤。

查看或下载示例代码如何下载

先决条件

警告

如果使用 Visual Studio 2017,请参阅 dotnet/sdk 问题 #3124,以了解无法与 Visual Studio 一起使用的 .NET Core SDK 版本的信息。

查看 ASP.NET 4.x Web API 项目

作为起点,本文使用ASP.NET Web API 2 入门中创建的ProductsApp项目。 在该项目中,简单的 ASP.NET 4.x Web API 项目配置如下。

Global.asax.cs中,调用 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);
        }
    }
}

WebApiConfig 类在App_Start文件夹中找到,并且具有静态 Register 方法:

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 }
            );
        }
    }
}

此类配置属性路由,不过实际上它并不是在项目中使用。 它还配置 ASP.NET Web API 使用的路由表。 在这种情况下,ASP.NET 4.x Web API 需要 Url 与格式 /api/{controller}/{id}{id} 是可选的。

ProductsApp项目包含一个控制器。 控制器继承自 ApiController,其中包含两个操作:

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);
        }
    }
}

ProductsController 使用的 Product 模型是一个简单的类:

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; }
    }
}

以下部分演示了如何将 Web API 项目迁移到 ASP.NET Core MVC。

创建目标项目

在 Visual Studio 中完成以下步骤:

  • 请参阅文件 > 的 > 项目 > > Visual Studio 解决方案中的其他项目类型 选择 "空白解决方案",并将解决方案命名为 " WebAPIMigration"。 单击 “确定” 按钮。
  • 将现有的ProductsApp项目添加到解决方案。
  • 向解决方案添加新的ASP.NET Core Web 应用程序项目。 从下拉选择 " .Net Core目标框架",然后选择 " API项目" 模板。 将项目命名为 " ProductsCore",然后单击 "确定" 按钮。

解决方案现在包含两个项目。 以下各节介绍了如何将ProductsApp项目的内容迁移到ProductsCore项目。

迁移配置

ASP.NET Core 不使用App_Start文件夹或global.asax文件,并且在发布时添加web.config文件。 Startup.csglobal.asa的替代,位于项目根目录中。 Startup 类处理所有应用启动任务。 有关更多信息,请参见ASP.NET Core 中的应用启动

在 ASP.NET Core MVC 中,当在 Startup.Configure中调用 UseMvc 时,默认情况下将包含特性路由。 以下 UseMvc 调用替换ProductsApp项目的App_Start/webapiconfig.cs文件:

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

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

迁移模型和控制器

复制ProductApp项目的控制器及其使用的模型。 按照以下步骤进行操作:

  1. 控制器/ProductsController从原始项目复制到新项目。
  2. 将整个模型文件夹从原始项目复制到新的项目。
  3. 更改复制的文件的命名空间,使其与新项目名称(ProductsCore)相匹配。 同时调整ProductsController.cs中的 using ProductsApp.Models; 语句。

此时,生成应用会导致大量编译错误。 之所以发生这些错误是因为 ASP.NET Core 中不存在下列组件:

  • ApiController
  • System.Web.Http 命名空间
  • IHttpActionResult 接口

修复错误,如下所示:

  1. 更改ApiControllerControllerBase 添加 using Microsoft.AspNetCore.Mvc; 以解析 ControllerBase 引用。
  2. 删除 using System.Web.Http;
  3. GetProduct 操作的返回类型从 IHttpActionResult 更改为 ActionResult<Product>

简化 GetProduct 操作的 return 语句,如下所示:

return product;

配置路由

按如下所示配置路由:

  1. 用以下特性标记 ProductsController 类:

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

    前面的[Route]属性配置控制器的属性路由模式。 [ApiController]特性使特性路由成为此控制器中所有操作的要求。

    特性路由支持令牌,如 [controller][action] 在运行时,每个标记分别替换为应用了属性的控制器或操作的名称。 这些标记减少了项目中的幻字符串的数目。 标记还可确保在应用自动重命名重构时,路由与相应的控制器和操作保持同步。

  2. 将项目的兼容模式设置为 ASP.NET Core 2.2:

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

    上述更改:

    • 需要在控制器级别使用 [ApiController] 特性。
    • 在 ASP.NET Core 2.2 中引入了可能中断的行为。
  3. 启用 ProductController 操作的 HTTP Get 请求:

    • GetAllProducts 操作应用[HttpGet]特性。
    • GetProduct 操作应用 [HttpGet("{id}")] 特性。

前面的更改和删除未使用的 using 语句后, ProductsController.cs文件如下所示:

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;
        }
    }
}

运行迁移的项目,并浏览到 /api/products 此时会显示三个产品的完整列表。 浏览到 /api/products/1 第一个产品随即出现。

兼容性填充码

AspNetCore库提供兼容性填充码,以将 ASP.NET 4.X Web API 项目移动到 ASP.NET Core。 兼容性填充码扩展 ASP.NET Core,以支持 ASP.NET 4.x Web API 2 中的一些约定。 本文档前面的示例移植的基本操作足以确保兼容性填充程序是不必要的。 对于较大的项目,使用兼容性填充码对于临时桥接 ASP.NET Core 与 ASP.NET 4.x Web API 2 之间的 API 间隙非常有用。

Web API 兼容性填充码旨在用作一种临时度量,以支持将大型 ASP.NET 4.x Web API 项目迁移到 ASP.NET Core。 随着时间推移,应更新项目以使用 ASP.NET Core 模式,而不是依靠兼容性填充码。

Microsoft.AspNetCore.Mvc.WebApiCompatShim 中包含的兼容性功能包括:

  • 添加 ApiController 类型,以便不需要更新控制器的基类型。
  • 启用 Web API 样式的模型绑定。 默认情况下,ASP.NET Core MVC 模型绑定函数与 ASP.NET 4.x MVC 5 的绑定函数类似。 兼容性填充码更改模型绑定,更类似于 ASP.NET 4.x Web API 2 模型绑定约定。 例如,复杂类型会自动从请求正文进行绑定。
  • 扩展模型绑定,以便控制器操作可以采用 HttpRequestMessage类型的参数。
  • 添加消息格式化程序,以允许操作返回 HttpResponseMessage类型的结果。
  • 添加 Web API 2 操作可能用于提供响应的其他响应方法:
    • HttpResponseMessage 生成器:
      • CreateResponse<T>
      • CreateErrorResponse
    • 操作结果方法:
      • BadRequestErrorMessageResult
      • ExceptionResult
      • InternalServerErrorResult
      • InvalidModelStateResult
      • NegotiatedContentResult
      • ResponseMessageResult
  • IContentNegotiator 的实例添加到应用的依赖项注入(DI)容器,并使WebApi中与内容协商相关的类型可用。 此类类型的示例包括 DefaultContentNegotiatorMediaTypeFormatter

使用兼容性填充码:

  1. 安装AspNetCore WebApiCompatShim NuGet 包。
  2. 通过在 Startup.ConfigureServices中调用 services.AddMvc().AddWebApiConventions(),将兼容性填充程序的服务注册到应用的 DI 容器。
  3. 使用应用的 IApplicationBuilder.UseMvc 调用中 IRouteBuilder 上的 MapWebApiRoute 定义特定于 web API 的路由。

其他资源