如何使用 AddMvcCore() 实现 "pure" ASP.NET Core Web API

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

我见过很多使用默认 AddMvc() 服务的 ASP.NET Core Web API 项目,却没有意识到使用 AddMvcCore() 是更好的选择,因为控制服务。

您究竟是如何通过使用 AddMvcCore() 实现 ASP.NET 核心 Web API 和 为什么它更好?

AddMvc()AddMvcCore()有什么区别?

首先要了解的关键是 AddMvc() 只是 AddMvcCore() 的 pre-loaded 版本。您可以在 GitHub repository.

查看 AddMvc() 扩展的确切实现

我和其他人一样喜欢使用默认的 VS 模板,但有时您需要知道什么时候它是错误的选择。我在网上看到一些指南更倾向于尝试 "undo" 这些默认服务,而不是仅仅使用一开始就没有实现它们的解决方案。

随着 ASP.NET 核心开源的出现,我们真的没有充分的理由不能剥离一层并在较低的层次上工作而不用担心丢失 "magic".


"minimal"和"pure"的定义

注意:这些定义仅用于此答案的上下文。主要是为了清楚起见并帮助进一步理解。

这个答案更倾向于 "pure" 而不是 "minimal"。我想描述一下原因,这样我在说什么就更清楚了。

最小。 "minimal" 解决方案将是 甚至不调用 AddMvcCore() 的实现完全没有方法。这样做的原因是,MVC 并不是真正的 "required" 组件来组装您自己的 Web API,并且它肯定会通过额外的依赖关系为您的代码增加一些重量。在这种情况下,由于您没有使用 AddMvcCore() 方法,因此您也不会将其注入到您的应用程序中,此处

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

这意味着映射您自己的路线并以您自己的方式响应 context。这确实一点都不具有挑战性,但我不想深入研究它,因为它相当 off-topic,但是 这里是一个最小实现的小小尝试

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

对于许多项目,"minimal" 方法意味着我们要放弃 MVC 中的一些功能。你真的必须权衡你的选择,看看你这个设计路径是否是正确的选择,因为在设计模式、便利性、可维护性、代码足迹以及最重要的性能和延迟之间存在平衡。 简单地说:"minimal" 解决方案意味着最小化代码和请求之间的服务和中间件。

Pure. 一个 "pure" 解决方案(就此答案的上下文而言)是避免 "pre-bundled" 出现的所有默认服务和中间件AddMvc() 一开始就不实施它。相反,我们使用 AddMvcCore(),这将在下一节中进一步解释:


使用AddMvcCore()

实现我们自己的服务/中间件

首先要开始的是设置 ConfigureServices 以使用 AddMvcCore()。如果您查看 GitHub repository,您可以看到 AddMvc() 使用一组标准的服务/中间件调用 AddMvcCore()

以下是一些突出的服务/中间件 "unneeded":

var builder = services.AddMvcCore();

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

这些默认服务中的许多对于一般的 Web 项目来说都很好,但对于 "pure" Web API.

通常是不受欢迎的

这里是 ConfigureServices 的示例实现,使用 AddMvcCore() 用于 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();
}

上面的实现大部分是 AddMvc() 扩展方法的副本,但是我添加了一些新区域,以便其他人可以看到这样做的额外好处。

  • Custom Input/Output Formatters. 在这里你可以做你自己的高度优化的序列化器(比如 Protobuf、Thrift、Avro 等)而不是使用 JSON(或更糟XML)序列化。
  • 请求Header处理。您可以确定Accept header是否被识别。
  • 授权处理。您可以实施自己的自定义授权,也可以利用 built-in 功能。
  • ApiExplorer。 对于某些项目,您可能会包含它,否则某些 WebAPI 可能不需要此功能。
  • Cross-Origin 请求 (CORS)。 如果您需要更宽松的 Web 安全性API,您可以启用它。

希望通过这个 "pure" 解决方案示例,您可以看到使用 AddMvcCore() 的好处,并乐于使用它。

如果您在 ASP.NET Core 的 Web 主机上工作时认真对待性能和延迟的控制,也许深入研究 "minimal" 解决方案是您正在处理的地方请求管道的边缘,而不是让它陷入 MVC 中间件的泥潭。


补充阅读

中间件管道的外观......根据我的定义,更少的层意味着 "minimal",而 "pure" 只是 MVC 的干净版本。

您可以在 Microsoft 文档中阅读更多相关信息:ASP.NET Core Middleware Fundamentals