{"id":611,"date":"2025-04-05T11:42:10","date_gmt":"2025-04-05T03:42:10","guid":{"rendered":"https:\/\/www.hyy.net\/?p=611"},"modified":"2025-04-05T11:42:10","modified_gmt":"2025-04-05T03:42:10","slug":"asp-net-core-in-action-20-creating-an-http-api-using-web-api-controllers","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=611","title":{"rendered":"ASP.NET Core in Action 20 Creating an HTTP API using web API controllers"},"content":{"rendered":"<p>20 Creating an HTTP API using web API controllers<br \/>\n20 \u4f7f\u7528 Web API \u63a7\u5236\u5668\u521b\u5efa HTTP API<\/p>\n<p>This chapter covers<br \/>\n\u672c\u7ae0\u6db5\u76d6<br \/>\n\u2022  Creating a web API controller to return JavaScript Object Notation (JSON) to clients<br \/>\n\u521b\u5efa Web API \u63a7\u5236\u5668\u4ee5\u5c06 JavaScript \u5bf9\u8c61\u8868\u793a\u6cd5 \uff08JSON\uff09 \u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef<br \/>\n\u2022  Using attribute routing to customize your URLs<br \/>\n\u4f7f\u7528\u5c5e\u6027\u8def\u7531\u81ea\u5b9a\u4e49 URL<br \/>\n\u2022  Generating a response using content negotiation<br \/>\n\u4f7f\u7528\u5185\u5bb9\u534f\u5546\u751f\u6210\u54cd\u5e94<br \/>\n\u2022  Applying common conventions with the [ApiController] attribute<br \/>\n\u4f7f\u7528 [ApiController] \u5c5e\u6027\u5e94\u7528\u5e38\u89c1\u7ea6\u5b9a<\/p>\n<p>In chapters 13 through 19 you worked through each layer of a server-side rendered ASP.NET Core application, using Razor Pages and Model-View-Controller (MVC) controllers to render HTML to the browser. In part 1 of this book you saw a different type of ASP.NET Core application, using minimal APIs to serve JSON for client-side SPAs or mobile apps. In this chapter you\u2019ll learn about web API controllers, which fit somewhere in between!<br \/>\n\u5728\u7b2c 13 \u7ae0\u5230\u7b2c 19 \u7ae0\u4e2d\uff0c\u60a8\u5b8c\u6210\u4e86 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u5448\u73b0\u7684\u670d\u52a1\u5668\u7aef\u7684\u6bcf\u4e00\u5c42\uff0c\u4f7f\u7528 Razor Pages \u548c\u6a21\u578b-\u89c6\u56fe-\u63a7\u5236\u5668 \uff08MVC\uff09 \u63a7\u5236\u5668\u5c06 HTML \u5448\u73b0\u5230\u6d4f\u89c8\u5668\u3002\u5728\u672c\u4e66\u7684\u7b2c 1 \u90e8\u5206\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u4e0d\u540c\u7c7b\u578b\u7684 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\uff0c\u5b83\u4f7f\u7528\u6700\u5c11\u7684 API \u4e3a\u5ba2\u6237\u7aef SPA \u6216\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b JSON\u3002\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3 Web API \u63a7\u5236\u5668\uff0c\u5b83\u4eec\u4ecb\u4e8e\u4e24\u8005\u4e4b\u95f4\uff01<\/p>\n<p>You can apply much of what you\u2019ve already learned to web API controllers; they use the same routing system as minimal APIs and the same MVC design pattern, model binding, and validation as Razor Pages.<br \/>\n\u60a8\u53ef\u4ee5\u5c06\u5df2\u7ecf\u5b66\u5230\u7684\u5927\u90e8\u5206\u77e5\u8bc6\u5e94\u7528\u5230 Web API \u63a7\u5236\u5668\u4e2d;\u5b83\u4eec\u4f7f\u7528\u4e0e\u6700\u5c0f API \u76f8\u540c\u7684\u8def\u7531\u7cfb\u7edf\uff0c\u4ee5\u53ca\u4e0e Razor Pages \u76f8\u540c\u7684 MVC \u8bbe\u8ba1\u6a21\u5f0f\u3001\u6a21\u578b\u7ed1\u5b9a\u548c\u9a8c\u8bc1\u3002<\/p>\n<p>In this chapter you\u2019ll learn how to define web API controllers and actions, and see how similar they are to the Razor Pages and controllers you already know. You\u2019ll learn how to create an API model to return data and HTTP status codes in response to a request, in a way that client apps can understand.<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5b9a\u4e49 Web API \u63a7\u5236\u5668\u548c\u4f5c\uff0c\u5e76\u4e86\u89e3\u5b83\u4eec\u4e0e\u60a8\u5df2\u7ecf\u77e5\u9053\u7684 Razor Pages \u548c\u63a7\u5236\u5668\u7684\u76f8\u4f3c\u4e4b\u5904\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u521b\u5efa API \u6a21\u578b\uff0c\u4ee5\u5ba2\u6237\u7aef\u5e94\u7528\u7a0b\u5e8f\u53ef\u4ee5\u7406\u89e3\u7684\u65b9\u5f0f\u8fd4\u56de\u6570\u636e\u548c HTTP \u72b6\u6001\u4ee3\u7801\u4ee5\u54cd\u5e94\u8bf7\u6c42\u3002<\/p>\n<p>After exploring how the MVC design pattern applies to web API controllers, you\u2019ll see how a related topic works with web APIs: routing. We\u2019ll look at how explicit attribute routing works with action methods, touching on many of the same concepts we covered in chapters 6 and 14.<br \/>\n\u5728\u63a2\u7d22\u4e86 MVC \u8bbe\u8ba1\u6a21\u5f0f\u5982\u4f55\u5e94\u7528\u4e8e Web API \u63a7\u5236\u5668\u4e4b\u540e\uff0c\u60a8\u5c06\u4e86\u89e3\u76f8\u5173\u4e3b\u9898\u5982\u4f55\u4e0e Web API \u914d\u5408\u4f7f\u7528\uff1a\u8def\u7531\u3002\u6211\u4eec\u5c06\u4e86\u89e3\u663e\u5f0f\u5c5e\u6027\u8def\u7531\u5982\u4f55\u4e0e action \u65b9\u6cd5\u4e00\u8d77\u5de5\u4f5c\uff0c\u5e76\u6d89\u53ca\u6211\u4eec\u5728\u7b2c 6 \u7ae0\u548c\u7b2c 14 \u7ae0\u4e2d\u4ecb\u7ecd\u7684\u8bb8\u591a\u76f8\u540c\u6982\u5ff5\u3002<\/p>\n<p>One of the big features added in ASP.NET Core 2.1 was the [ApiController] attribute. This attribute applies several common conventions used in web APIs, reducing the amount of code you must write yourself. In section 20.5 you\u2019ll learn how automatic 400 Bad Requests for invalid requests, model-binding parameter inference, and ProblemDetails support make building APIs easier and more consistent.<br \/>\nASP.NET Core 2.1 \u4e2d\u6dfb\u52a0\u7684\u91cd\u8981\u529f\u80fd\u4e4b\u4e00\u662f [ApiController] \u5c5e\u6027\u3002\u6b64\u5c5e\u6027\u5e94\u7528 Web API \u4e2d\u4f7f\u7528\u7684\u51e0\u4e2a\u5e38\u89c1\u7ea6\u5b9a\uff0c\u4ece\u800c\u51cf\u5c11\u60a8\u5fc5\u987b\u81ea\u5df1\u7f16\u5199\u7684\u4ee3\u7801\u91cf\u3002\u5728\u7b2c 20.5 \u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u9488\u5bf9\u65e0\u6548\u8bf7\u6c42\u7684\u81ea\u52a8 400 \u9519\u8bef\u8bf7\u6c42\u3001\u6a21\u578b\u7ed1\u5b9a\u53c2\u6570\u63a8\u7406\u548c ProblemDetails \u652f\u6301\u5982\u4f55\u4f7f\u6784\u5efa API \u66f4\u8f7b\u677e\u3001\u66f4\u4e00\u81f4\u3002<\/p>\n<p>You\u2019ll also learn how to format the API models returned by your action methods using content negotiation, to ensure that you generate a response that the calling client can understand. As part of this, you\u2019ll learn how to add support for additional format types, such as Extensible Markup Language (XML), so that you can generate XML responses if the client requests it.<br \/>\n\u60a8\u8fd8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528\u5185\u5bb9\u534f\u5546\u6765\u683c\u5f0f\u5316\u4f5c\u65b9\u6cd5\u8fd4\u56de\u7684 API \u6a21\u578b\uff0c\u4ee5\u786e\u4fdd\u60a8\u751f\u6210\u7684\u54cd\u5e94\u662f\u8c03\u7528\u5ba2\u6237\u7aef\u53ef\u4ee5\u7406\u89e3\u7684\u3002\u4f5c\u4e3a\u5176\u4e2d\u7684\u4e00\u90e8\u5206\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u6dfb\u52a0\u5bf9\u5176\u4ed6\u683c\u5f0f\u7c7b\u578b\uff08\u5982\u53ef\u6269\u5c55\u6807\u8bb0\u8bed\u8a00 \uff08XML\uff09\uff09\u7684\u652f\u6301\uff0c\u4ee5\u4fbf\u5728\u5ba2\u6237\u7aef\u8bf7\u6c42\u65f6\u751f\u6210 XML \u54cd\u5e94\u3002<\/p>\n<p>Finally, I discuss some of the differences between API controllers and minimal API applications, and when you should choose one over the other. Before we get to that, we look at how to get started. In section 20.1 you\u2019ll see how to create a web API project and add your first API controller.<br \/>\n\u6700\u540e\uff0c\u6211\u5c06\u8ba8\u8bba API \u63a7\u5236\u5668\u548c\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u4e4b\u95f4\u7684\u4e00\u4e9b\u5dee\u5f02\uff0c\u4ee5\u53ca\u4f55\u65f6\u5e94\u8be5\u9009\u62e9\u4e00\u4e2a\u800c\u4e0d\u662f\u53e6\u4e00\u4e2a\u3002\u5728\u5f00\u59cb\u4e4b\u524d\uff0c\u6211\u4eec\u5148\u770b\u770b\u5982\u4f55\u5f00\u59cb\u3002\u5728 Section 20.1 \u4e2d\uff0c\u60a8\u5c06\u770b\u5230\u5982\u4f55\u521b\u5efa Web API \u9879\u76ee\u5e76\u6dfb\u52a0\u60a8\u7684\u7b2c\u4e00\u4e2a API \u63a7\u5236\u5668\u3002<\/p>\n<h2>20.1 Creating your first web API project<\/h2>\n<p>20.1 \u521b\u5efa\u60a8\u7684\u7b2c\u4e00\u4e2a Web API \u9879\u76ee<\/p>\n<p>In this section you\u2019ll learn how to create an ASP.NET Core web API project and create your first web API controllers. You\u2019ll see how to use controller action methods to handle HTTP requests and how to use ActionResults to generate a response.<br \/>\n\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u521b\u5efa ASP.NET Core Web API \u9879\u76ee\u5e76\u521b\u5efa\u4f60\u7684\u7b2c\u4e00\u4e2a Web API \u63a7\u5236\u5668\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528\u63a7\u5236\u5668\u4f5c\u65b9\u6cd5\u5904\u7406 HTTP \u8bf7\u6c42\uff0c\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528 ActionResults \u751f\u6210\u54cd\u5e94\u3002<\/p>\n<p><b>NOTE<\/b> as I mentioned previously that a web API project is a standard ASP.NET Core project, which uses the MVC framework and web API controllers.<br \/>\n\u6ce8\u610f:\u5982\u524d\u6240\u8ff0\uff0cWeb API \u9879\u76ee\u662f\u6807\u51c6\u7684 ASP.NET Core \u9879\u76ee\uff0c\u5b83\u4f7f\u7528 MVC \u6846\u67b6\u548c Web API \u63a7\u5236\u5668\u3002<\/p>\n<p>Some people think of the MVC design pattern as applying only to applications that render their UI directly, like the Razor views you\u2019ve seen in previous chapters or MVC controllers with Razor views. However, in ASP.NET Core, I feel the MVC pattern applies equally well when building a web API. For web APIs, the view part of the MVC pattern involves generating a machine-friendly response rather than a user-friendly response.<br \/>\n\u6709\u4e9b\u4eba\u8ba4\u4e3a MVC \u8bbe\u8ba1\u6a21\u5f0f\u4ec5\u9002\u7528\u4e8e\u76f4\u63a5\u5448\u73b0\u5176 UI \u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f8b\u5982\u60a8\u5728\u524d\u51e0\u7ae0\u4e2d\u770b\u5230\u7684 Razor \u89c6\u56fe\u6216\u5177\u6709 Razor \u89c6\u56fe\u7684 MVC \u63a7\u5236\u5668\u3002\u4f46\u662f\uff0c\u5728 ASP.NET Core \u4e2d\uff0c\u6211\u89c9\u5f97 MVC \u6a21\u5f0f\u5728\u6784\u5efa Web API \u65f6\u540c\u6837\u9002\u7528\u3002\u5bf9\u4e8e Web API\uff0cMVC \u6a21\u5f0f\u7684\u89c6\u56fe\u90e8\u5206\u6d89\u53ca\u751f\u6210\u8ba1\u7b97\u673a\u53cb\u597d\u7684\u54cd\u5e94\uff0c\u800c\u4e0d\u662f\u7528\u6237\u53cb\u597d\u7684\u54cd\u5e94\u3002<\/p>\n<p>As a parallel to this, you create web API controllers in ASP.NET Core in the same way you create traditional MVC controllers. The only thing that differentiates them from a code perspective is the type of data they return. MVC controllers typically return a ViewResult; web API controllers generally return raw .NET objects from their action methods, or an IActionResult instance such as StatusCodeResult, as you saw in chapter 15.<br \/>\n\u4e0e\u6b64\u5e76\u884c\uff0c\u60a8\u53ef\u4ee5\u5728 ASP.NET Core \u4e2d\u521b\u5efa Web API \u63a7\u5236\u5668\uff0c\u5176\u65b9\u5f0f\u4e0e\u521b\u5efa\u4f20\u7edf MVC \u63a7\u5236\u5668\u7684\u65b9\u5f0f\u76f8\u540c\u3002\u4ece\u4ee3\u7801\u89d2\u5ea6\u6765\u770b\uff0c\u5b83\u4eec\u7684\u552f\u4e00\u533a\u522b\u662f\u5b83\u4eec\u8fd4\u56de\u7684\u6570\u636e\u7c7b\u578b\u3002MVC \u63a7\u5236\u5668\u901a\u5e38\u8fd4\u56de ViewResult;Web API \u63a7\u5236\u5668\u901a\u5e38\u4ece\u5176\u4f5c\u65b9\u6cd5\u6216 IActionResult \u5b9e\u4f8b\uff08\u5982 StatusCodeResult\uff09\u8fd4\u56de\u539f\u59cb .NET \u5bf9\u8c61\uff0c\u5982\u7b2c 15 \u7ae0\u6240\u793a\u3002<\/p>\n<p>You can create a new web API project in Visual Studio using the same process you\u2019ve seen previously in Visual Studio. Choose File &gt; New, and in the Create a new project dialog box, select the ASP.NET Core Web API template. Enter your project name in the Configure your new project dialog box, and review the Additional information box, shown in figure 20.1, before choosing Create. If you\u2019re using the command-line interface (CLI), you can create a similar template using dotnet new webapi.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528\u4e4b\u524d\u5728 Visual Studio \u4e2d\u770b\u5230\u7684\u76f8\u540c\u8fc7\u7a0b\u5728 Visual Studio \u4e2d\u521b\u5efa\u65b0\u7684 Web API \u9879\u76ee\u3002\u9009\u62e9 File &gt; New\uff0c\u7136\u540e\u5728 Create a new project \uff08\u521b\u5efa\u65b0\u9879\u76ee\uff09 \u5bf9\u8bdd\u6846\u4e2d\uff0c\u9009\u62e9 ASP.NET Core Web API \u6a21\u677f\u3002\u5728 Configure your new project \uff08\u914d\u7f6e\u60a8\u7684\u65b0\u9879\u76ee\uff09 \u5bf9\u8bdd\u6846\u4e2d\u8f93\u5165\u60a8\u7684\u9879\u76ee\u540d\u79f0\uff0c\u7136\u540e\u67e5\u770b Additional information \uff08\u5176\u4ed6\u4fe1\u606f\uff09 \u6846\uff0c\u5982\u56fe 20.1 \u6240\u793a\uff0c\u7136\u540e\u9009\u62e9 Create \uff08\u521b\u5efa\uff09\u3002\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f\u547d\u4ee4\u884c\u754c\u9762 \uff08CLI\uff09\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528 dotnet new webapi \u521b\u5efa\u7c7b\u4f3c\u7684\u6a21\u677f\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2001.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.1 The Additional information screen. This screen follows on from the Configure your new project dialog box and lets you customize the template that generates your application.<br \/>\n\u56fe 20.1 \u201c\u5176\u4ed6\u4fe1\u606f\u201d\u5c4f\u5e55\u3002\u6b64\u5c4f\u5e55\u662f Configure your new project \u5bf9\u8bdd\u6846\u7684\u540e\u7eed\u5c4f\u5e55\uff0c\u5141\u8bb8\u60a8\u81ea\u5b9a\u4e49\u751f\u6210\u5e94\u7528\u7a0b\u5e8f\u7684\u6a21\u677f\u3002<\/p>\n<p>The web API template configures the ASP.NET Core project for web API controllers only in Program.cs, as shown in listing 20.1. If you compare this template with the MVC controller project in chapter 19, you\u2019ll see that the web API project uses AddControllers() instead of AddControllersWithViews(). This adds only the services needed for controllers but omits the services for rendering Razor views. Also, the API controllers are added using MapControllers() instead of MapControllerRoute(), as web API controller typically use explicit routing instead of conventional routing. The default web API template also adds the OpenAPI services and endpoints required by the Swagger UI, as you saw in chapter 11.<br \/>\nWeb API \u6a21\u677f\u4ec5\u5728 Program.cs \u4e2d\u4e3a Web API \u63a7\u5236\u5668\u914d\u7f6e ASP.NET Core \u9879\u76ee\uff0c\u5982\u6e05\u5355 20.1 \u6240\u793a\u3002\u5982\u679c\u5c06\u6b64\u6a21\u677f\u4e0e\u7b2c 19 \u7ae0\u4e2d\u7684 MVC \u63a7\u5236\u5668\u9879\u76ee\u8fdb\u884c\u6bd4\u8f83\uff0c\u4f60\u5c06\u770b\u5230 Web API \u9879\u76ee\u4f7f\u7528 AddControllers\uff08\uff09 \u800c\u4e0d\u662f AddControllersWithViews\uff08\uff09\u3002\u8fd9\u4ec5\u6dfb\u52a0\u63a7\u5236\u5668\u6240\u9700\u7684\u670d\u52a1\uff0c\u4f46\u7701\u7565\u4e86\u7528\u4e8e\u5448\u73b0 Razor \u89c6\u56fe\u7684\u670d\u52a1\u3002\u6b64\u5916\uff0cAPI \u63a7\u5236\u5668\u662f\u4f7f\u7528 MapControllers\uff08\uff09 \u800c\u4e0d\u662f MapControllerRoute\uff08\uff09 \u6dfb\u52a0\u7684\uff0c\u56e0\u4e3a Web API \u63a7\u5236\u5668\u901a\u5e38\u4f7f\u7528\u663e\u5f0f\u8def\u7531\u800c\u4e0d\u662f\u4f20\u7edf\u8def\u7531\u3002\u9ed8\u8ba4 Web API \u6a21\u677f\u8fd8\u6dfb\u52a0\u4e86 Swagger UI \u6240\u9700\u7684 OpenAPI \u670d\u52a1\u548c\u7aef\u70b9\uff0c\u5982\u7b2c 11 \u7ae0\u6240\u793a\u3002<\/p>\n<p>Listing 20.1 Program.cs for the default web API project<br \/>\n\u6e05\u5355 20.1 \u9ed8\u8ba4 Web API \u9879\u76ee\u7684 Program.cs<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers();  #A\n\nbuilder.Services.AddEndpointsApiExplorer();    #B\nbuilder.Services.AddSwaggerGen();    #B\n\nWebApplication app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();    #C\n    app.UseSwaggerUI();  #C\n}\n\napp.UseHttpsRedirection();\napp.UseAuthorization();\n\napp.MapControllers();  #D\n\napp.Run();<\/code><\/pre>\n<p>\u2776 AddControllers adds the necessary services for web API controllers to your application.<br \/>\nAddControllers \u5c06 Web API \u63a7\u5236\u5668\u7684\u5fc5\u8981\u670d\u52a1\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u3002<br \/>\n\u2777 Adds services required to generate the Swagger\/OpenAPI specification document<br \/>\n\u6dfb\u52a0\u751f\u6210 Swagger\/OpenAPI \u89c4\u8303\u6587\u6863\u6240\u9700\u7684\u670d\u52a1<br \/>\n\u2778 Adds Swagger UI middleware for exploring your web API endpoints<br \/>\n\u6dfb\u52a0\u7528\u4e8e\u63a2\u7d22 Web API \u7aef\u70b9\u7684 Swagger UI \u4e2d\u95f4\u4ef6<br \/>\n\u2779 MapControllers configures the web API controller actions in your app as endpoints.<br \/>\nMapControllers \u5c06\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 Web API \u63a7\u5236\u5668\u4f5c\u914d\u7f6e\u4e3a\u7aef\u70b9\u3002<\/p>\n<p>The program in listing 20.1 instructs your application to find all the web API controllers in your application and configure them in the EndpointMiddleware. Each action method becomes an endpoint and can receive requests when the RoutingMiddleware maps an incoming URL to the action method.<br \/>\n\u6e05\u5355 20.1 \u4e2d\u7684\u7a0b\u5e8f\u6307\u793a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u67e5\u627e\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709 Web API \u63a7\u5236\u5668\uff0c\u5e76\u5728 EndpointMiddleware \u4e2d\u914d\u7f6e\u5b83\u4eec\u3002\u6bcf\u4e2a\u4f5c\u65b9\u6cd5\u90fd\u6210\u4e3a\u4e00\u4e2a\u7aef\u70b9\uff0c\u5f53 RoutingMiddleware \u5c06\u4f20\u5165\u7684 URL \u6620\u5c04\u5230\u4f5c\u65b9\u6cd5\u65f6\uff0c\u53ef\u4ee5\u63a5\u6536\u8bf7\u6c42\u3002<\/p>\n<p><b>NOTE<\/b> Technically, you can include Razor Pages, minimal APIs, and web API controllers in the same app, but I prefer to keep them separate where possible. There are certain aspects (such as error handling and authentication) that are made easier by keeping them separate. Of course, running two separate applications has its own difficulties!<br \/>\n\u6ce8\u610f:\u4ece\u6280\u672f\u4e0a\u8bb2\uff0c\u60a8\u53ef\u4ee5\u5728\u540c\u4e00\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5305\u542b Razor Pages\u3001\u6700\u5c0f API \u548c Web API \u63a7\u5236\u5668\uff0c\u4f46\u6211\u66f4\u559c\u6b22\u5c3d\u53ef\u80fd\u5c06\u5b83\u4eec\u5206\u5f00\u3002\u901a\u8fc7\u5c06\u67d0\u4e9b\u65b9\u9762 \uff08\u4f8b\u5982\u9519\u8bef\u5904\u7406\u548c\u8eab\u4efd\u9a8c\u8bc1\uff09 \u5206\u5f00\uff0c\u53ef\u4ee5\u66f4\u8f7b\u677e\u5730\u4f7f\u7528\u5b83\u4eec\u3002\u5f53\u7136\uff0c\u8fd0\u884c\u4e24\u4e2a\u5355\u72ec\u7684\u5e94\u7528\u7a0b\u5e8f\u6709\u5176\u81ea\u8eab\u7684\u56f0\u96be\uff01<\/p>\n<p>You can add a web API controller to your project by creating a new .cs file anywhere in your project. Traditionally, this file is placed in a folder called Controllers, but that\u2019s not a technical requirement.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u9879\u76ee\u4e2d\u7684\u4efb\u4f55\u4f4d\u7f6e\u521b\u5efa\u65b0\u7684 .cs \u6587\u4ef6\uff0c\u5c06 Web API \u63a7\u5236\u5668\u6dfb\u52a0\u5230\u9879\u76ee\u4e2d\u3002\u4f20\u7edf\u4e0a\uff0c\u6b64\u6587\u4ef6\u653e\u7f6e\u5728\u540d\u4e3a Controllers \u7684\u6587\u4ef6\u5939\u4e2d\uff0c\u4f46\u8fd9\u4e0d\u662f\u6280\u672f\u8981\u6c42\u3002<\/p>\n<p><b>Tip<\/b> Vertical slice architecture and feature folders are (fortunately) becoming more popular in .NET circles. With these approaches, you organize your project based on features instead of technical concepts like controllers and models.<br \/>\n\u63d0\u793a:\u5782\u76f4\u5207\u7247\u4f53\u7cfb\u7ed3\u6784\u548c\u529f\u80fd\u6587\u4ef6\u5939\uff08\u5e78\u8fd0\u7684\u662f\uff09\u5728 .NET \u5708\u5b50\u4e2d\u8d8a\u6765\u8d8a\u6d41\u884c\u3002\u4f7f\u7528\u8fd9\u4e9b\u65b9\u6cd5\uff0c\u60a8\u53ef\u4ee5\u6839\u636e\u529f\u80fd\u800c\u4e0d\u662f\u63a7\u5236\u5668\u548c\u6a21\u578b\u7b49\u6280\u672f\u6982\u5ff5\u6765\u7ec4\u7ec7\u9879\u76ee\u3002<\/p>\n<p>Listing 20.2 shows an example of a simple controller, with a single endpoint, that returns an IEnumerable<string> when executed. This example highlights the similarity with traditional MVC controllers (using action methods and a base class) and minimal APIs (returning the response object directly to be serialized later).<br \/>\n\u6e05\u5355 20.2 \u5c55\u793a\u4e86\u4e00\u4e2a\u7b80\u5355\u7684\u63a7\u5236\u5668\u793a\u4f8b\uff0c\u5b83\u53ea\u6709\u4e00\u4e2a\u7aef\u70b9\uff0c\u6267\u884c\u65f6\u8fd4\u56de\u4e00\u4e2a IEnumerable\u3002\u6b64\u793a\u4f8b\u7a81\u51fa\u4e86\u4e0e\u4f20\u7edf MVC \u63a7\u5236\u5668\uff08\u4f7f\u7528\u4f5c\u65b9\u6cd5\u548c\u57fa\u7c7b\uff09\u548c\u6700\u5c0f API\uff08\u76f4\u63a5\u8fd4\u56de\u54cd\u5e94\u5bf9\u8c61\u4ee5\u4f9b\u7a0d\u540e\u5e8f\u5217\u5316\uff09\u7684\u76f8\u4f3c\u6027\u3002<\/p>\n<p>Listing 20.2 A simple web API controller<\/p>\n<pre><code>[ApiController]    #A\npublic class FruitController : ControllerBase         #B\n{\n    List&lt;string&gt; _fruit = new List&lt;string&gt;    #C\n    {                                         #C\n        &quot;Pear&quot;,                               #C\n        &quot;Lemon&quot;,                              #C\n        &quot;Peach&quot;                               #C\n    };                                        #C\n    [HttpGet(&quot;fruit&quot;)]                           #D\n    public IEnumerable&lt;string&gt; Index()      #E\n    {                      #F\n        return _fruit;     #F\n    }                      #F\n}<\/code><\/pre>\n<p>\u2776 The [ApiController] attribute opts in to common conventions.<br \/>\n[ApiController] \u5c5e\u6027\u9009\u62e9\u52a0\u5165\u5e38\u89c1\u7ea6\u5b9a\u3002<br \/>\n\u2777 The ControllerBase class provides helper functions.<br \/>\nControllerBase \u7c7b\u63d0\u4f9b\u4e86\u5e2e\u52a9\u7a0b\u5e8f\u51fd\u6570\u3002<br \/>\n\u2778 This would typically come from a dependency injection (DI) injected service instead.<br \/>\n\u8fd9\u901a\u5e38\u6765\u81ea\u4f9d\u8d56\u6ce8\u5165 \uff08DI\uff09 \u6ce8\u5165\u7684\u670d\u52a1\u3002<br \/>\n\u2779 The [HttpGet] attribute defines the route template used to call the action.<br \/>\n[HttpGet] \u5c5e\u6027\u5b9a\u4e49\u7528\u4e8e\u8c03\u7528\u4f5c\u7684\u8def\u7531\u6a21\u677f\u3002<br \/>\n\u277a The name of the action method, Index, isn\u2019t used for routing. It can be anything you like.<br \/>\naction\u65b9\u6cd5\u7684\u540d\u79f0 Index \u4e0d\u7528\u4e8e\u8def\u7531\u3002\u5b83\u53ef\u4ee5\u662f\u4f60\u559c\u6b22\u7684\u4efb\u4f55\u4e1c\u897f\u3002<br \/>\n\u277b The controller exposes a single action method that returns the list of fruit.<br \/>\n\u63a7\u5236\u5668\u516c\u5f00\u4e00\u4e2a\u8fd4\u56de fruit \u5217\u8868\u7684 action \u65b9\u6cd5\u3002<\/p>\n<p>When invoked, this endpoint returns the list of strings serialized to JSON, as shown in figure 20.2.<br \/>\n\u8c03\u7528\u65f6\uff0c\u6b64\u7ec8\u7aef\u8282\u70b9\u8fd4\u56de\u5e8f\u5217\u5316\u4e3a JSON \u7684\u5b57\u7b26\u4e32\u5217\u8868\uff0c\u5982\u56fe 20.2 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2002.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.2 Testing the web API in listing 20.2 by accessing the URL in the browser. A GET request is made to the \/fruit URL, which returns a <code>List&lt;string&gt;<\/code> that is serialized to JSON.<br \/>\n\u56fe 20.2 \u901a\u8fc7\u5728\u6d4f\u89c8\u5668\u4e2d\u8bbf\u95ee URL \u6765\u6d4b\u8bd5\u6e05\u5355 20.2 \u4e2d\u7684 Web API\u3002\u5411 \/fruit URL \u53d1\u51fa GET \u8bf7\u6c42\uff0c\u8be5\u8bf7\u6c42\u8fd4\u56de\u5e8f\u5217\u5316\u4e3a JSON \u7684 <code>List&lt;string&gt;<\/code>\u3002<\/p>\n<p>Web API controllers typically use the [ApiController] attribute (introduced in .NET Core 2.1) and derive from the ControllerBase class. The base class provides several helper methods for generating results, and the [ApiController] attribute automatically applies some common conventions, as you\u2019ll see in section 20.5.<br \/>\nWeb API \u63a7\u5236\u5668\u901a\u5e38\u4f7f\u7528 [ApiController] \u5c5e\u6027\uff08\u5728 .NET Core 2.1 \u4e2d\u5f15\u5165\uff09\u5e76\u4ece ControllerBase \u7c7b\u6d3e\u751f\u3002\u57fa\u7c7b\u63d0\u4f9b\u4e86\u51e0\u4e2a\u7528\u4e8e\u751f\u6210\u7ed3\u679c\u7684\u5e2e\u52a9\u7a0b\u5e8f\u65b9\u6cd5\uff0c\u5e76\u4e14 [ApiController] \u5c5e\u6027\u4f1a\u81ea\u52a8\u5e94\u7528\u4e00\u4e9b\u5e38\u89c1\u7ea6\u5b9a\uff0c\u5982\u7b2c 20.5 \u8282\u6240\u793a\u3002<\/p>\n<p><b>Tip<\/b> The Controller base class is typically used when you use MVC controllers with Razor views. You don\u2019t need to return Razor views with web API controllers, so ControllerBase is the better option.<br \/>\n\u63d0\u793a:\u5c06 Controller \u57fa\u7c7b\u901a\u5e38\u7528\u4e8e Razor \u89c6\u56fe\u7684 MVC \u63a7\u5236\u5668\u3002\u65e0\u9700\u4f7f\u7528 Web API \u63a7\u5236\u5668\u8fd4\u56de Razor \u89c6\u56fe\uff0c\u56e0\u6b64 ControllerBase \u662f\u66f4\u597d\u7684\u9009\u62e9\u3002<\/p>\n<p>In listing 20.2 you can see that the action method, Index, returns a list of strings directly from the action method. When you return data from an action like this, you\u2019re providing the API model for the request. The client will receive this data. It\u2019s formatted into an appropriate response, a JSON representation of the list in the case of figure 20.2, and sent back to the browser with a 200 OK status code.<br \/>\n\u5728\u6e05\u5355 20.2 \u4e2d\uff0c\u4f60\u53ef\u4ee5\u770b\u5230\u4f5c\u65b9\u6cd5 Index \u76f4\u63a5\u4ece\u4f5c\u65b9\u6cd5\u8fd4\u56de\u4e00\u4e2a\u5b57\u7b26\u4e32\u5217\u8868\u3002\u5f53\u60a8\u4ece\u6b64\u7c7b\u4f5c\u8fd4\u56de\u6570\u636e\u65f6\uff0c\u60a8\u5c06\u4e3a\u8bf7\u6c42\u63d0\u4f9b API \u6a21\u578b\u3002\u5ba2\u6237\u7aef\u5c06\u6536\u5230\u6b64\u6570\u636e\u3002\u5b83\u88ab\u683c\u5f0f\u5316\u4e3a\u9002\u5f53\u7684\u54cd\u5e94\uff0c\u5373\u56fe 20.2 \u4e2d\u5217\u8868\u7684 JSON \u8868\u793a\u5f62\u5f0f\uff0c\u5e76\u4f7f\u7528 200 OK \u72b6\u6001\u4ee3\u7801\u53d1\u9001\u56de\u6d4f\u89c8\u5668\u3002<\/p>\n<p><b>Tip<\/b> Web API controllers format data as JSON by default. You\u2019ll see how to format the returned data in other ways in section 20.6. Minimal API endpoints that return data directly (rather than via an IResult) will format data only as JSON; there are no other options.<br \/>\n\u63d0\u793a:\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cWeb API \u63a7\u5236\u5668\u5c06\u6570\u636e\u683c\u5f0f\u5316\u4e3a JSON\u3002\u60a8\u5c06\u5728 Section 20.6 \u4e2d\u770b\u5230\u5982\u4f55\u4ee5\u5176\u4ed6\u65b9\u5f0f\u683c\u5f0f\u5316\u8fd4\u56de\u7684\u6570\u636e\u3002\u76f4\u63a5\u8fd4\u56de\u6570\u636e\uff08\u800c\u4e0d\u662f\u901a\u8fc7 IResult\uff09\u7684\u6700\u5c0f API \u7aef\u70b9\u5c06\u4ec5\u5c06\u6570\u636e\u683c\u5f0f\u5316\u4e3a JSON;\u6ca1\u6709\u5176\u4ed6\u9009\u62e9\u3002<\/p>\n<p>The URL at which a web API controller action is exposed is handled in the same way as for traditional MVC controllers and Razor Pages: using routing. The [HttpGet(&quot;fruit&quot;)] attribute applied to the Index method indicates that the method should use the route template &quot;fruit&quot; and should respond to HTTP GET requests. You\u2019ll learn more about attribute routing in section 20.4, but it\u2019s similar to the minimal API routing that you\u2019re already familiar with.<br \/>\n\u516c\u5f00 Web API \u63a7\u5236\u5668\u4f5c\u7684 URL \u7684\u5904\u7406\u65b9\u5f0f\u4e0e\u4f20\u7edf MVC \u63a7\u5236\u5668\u548c Razor Pages \u7684\u5904\u7406\u65b9\u5f0f\u76f8\u540c\uff1a\u4f7f\u7528\u8def\u7531\u3002\u5e94\u7528\u4e8e Index \u65b9\u6cd5\u7684 [HttpGet\uff08\u201cfruit\u201d\uff09] \u5c5e\u6027\u6307\u793a\u8be5\u65b9\u6cd5\u5e94\u4f7f\u7528\u8def\u7531\u6a21\u677f \u201cfruit\u201d \u5e76\u5e94\u54cd\u5e94 HTTP GET \u8bf7\u6c42\u3002\u60a8\u5c06\u5728 Section 20.4 \u4e2d\u4e86\u89e3\u6709\u5173 attribute routing \u7684\u66f4\u591a\u4fe1\u606f\uff0c\u4f46\u5b83\u7c7b\u4f3c\u4e8e\u60a8\u5df2\u7ecf\u719f\u6089\u7684\u6700\u5c0f API \u8def\u7531\u3002<\/p>\n<p>In listing 20.2 data is returned directly from the action method, but you don\u2019t have to do that. You\u2019re free to return an IActionResult instead, and often this is required. Depending on the desired behavior of your API, you sometimes want to return data, and other times you may want to return a raw HTTP status code, indicating whether the request was successful. For example, if an API call is made requesting details of a product that does not exist, you might want to return a 404 Not Found status code.<br \/>\n\u5728\u793a\u4f8b 20.2 \u4e2d\uff0c\u6570\u636e\u76f4\u63a5\u4ece action \u65b9\u6cd5\u8fd4\u56de\uff0c\u4f46\u4f60\u4e0d\u5fc5\u8fd9\u6837\u505a\u3002\u60a8\u53ef\u4ee5\u81ea\u7531\u5730\u8fd4\u56de IActionResult\uff0c\u8fd9\u901a\u5e38\u662f\u5fc5\u9700\u7684\u3002\u6839\u636e API \u7684\u6240\u9700\u884c\u4e3a\uff0c\u60a8\u6709\u65f6\u5e0c\u671b\u8fd4\u56de\u6570\u636e\uff0c\u6709\u65f6\u60a8\u53ef\u80fd\u5e0c\u671b\u8fd4\u56de\u539f\u59cb HTTP \u72b6\u6001\u4ee3\u7801\uff0c\u4ee5\u6307\u793a\u8bf7\u6c42\u662f\u5426\u6210\u529f\u3002\u4f8b\u5982\uff0c\u5982\u679c\u8fdb\u884c API \u8c03\u7528\uff0c\u8bf7\u6c42\u4e0d\u5b58\u5728\u7684\u4ea7\u54c1\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u5219\u53ef\u80fd\u9700\u8981\u8fd4\u56de 404 Not Found \u72b6\u6001\u4ee3\u7801\u3002<\/p>\n<p><b>NOTE<\/b> This is similar to the patterns you used in minimal APIs. But remember, minimal APIs use IResult, web API controllers, MVC controllers, and Razor Pages use IActionResult.<br \/>\n\u6ce8\u610f:\u8fd9\u7c7b\u4f3c\u4e8e\u60a8\u5728\u6700\u5c0f API \u4e2d\u4f7f\u7528\u7684\u6a21\u5f0f\u3002\u4f46\u8bf7\u8bb0\u4f4f\uff0c\u6700\u5c0f\u7684 API \u4f7f\u7528 IResult\uff0cWeb API \u63a7\u5236\u5668\u3001MVC \u63a7\u5236\u5668\u548c Razor \u9875\u9762\u4f7f\u7528 IActionResult\u3002<\/p>\n<p>Listing 20.3 shows an example of where you must return an IActionResult. It shows another action on the same FruitController as before. This method exposes a way for clients to fetch a specific fruit by an id, which we\u2019ll assume for this example is an index into the list of _fruit you defined in the previous listing. Model binding is used to set the value of the id parameter from the request.<br \/>\n\u6e05\u5355 20.3 \u663e\u793a\u4e86\u4e00\u4e2a\u5fc5\u987b\u8fd4\u56de IActionResult \u7684\u793a\u4f8b\u3002\u5b83\u663e\u793a\u4e86\u4e0e\u4ee5\u524d\u4e00\u6837\uff0c\u5728\u540c\u4e00\u4e2a FruitController \u4e0a\u7684\u53e6\u4e00\u4e2a\u4f5c\u3002\u6b64\u65b9\u6cd5\u4e3a Client \u7aef\u63d0\u4f9b\u4e86\u4e00\u79cd\u901a\u8fc7 id \u83b7\u53d6\u7279\u5b9a\u6c34\u679c\u7684\u65b9\u6cd5\uff0c\u5728\u672c\u4f8b\u4e2d\uff0c\u6211\u4eec\u5047\u8bbe\u8be5 ID \u662f\u60a8\u5728\u4e0a\u4e00\u4e2a\u6e05\u5355\u4e2d\u5b9a\u4e49\u7684 _fruit \u5217\u8868\u7684\u7d22\u5f15\u3002\u6a21\u578b\u7ed1\u5b9a\u7528\u4e8e\u8bbe\u7f6e\u8bf7\u6c42\u4e2d id \u53c2\u6570\u7684\u503c\u3002<\/p>\n<p><b>NOTE<\/b> API controllers use the same model binding infrastructure as Razor Pages to bind action method parameters to the incoming request. Model binding and validation work the same way you saw in chapter 16: you can bind the request to simple primitives, as well as to complex C# objects. The only difference is that there isn\u2019t a PageModel with [BindProperty] properties; you can bind only to action method parameters.<br \/>\n\u6ce8\u610f:API \u63a7\u5236\u5668\u4f7f\u7528\u4e0e Razor Pages \u76f8\u540c\u7684\u6a21\u578b\u7ed1\u5b9a\u57fa\u7840\u7ed3\u6784\u5c06\u4f5c\u65b9\u6cd5\u53c2\u6570\u7ed1\u5b9a\u5230\u4f20\u5165\u8bf7\u6c42\u3002\u6a21\u578b\u7ed1\u5b9a\u548c\u9a8c\u8bc1\u7684\u5de5\u4f5c\u65b9\u5f0f\u4e0e\u7b2c 16 \u7ae0\u4e2d\u4ecb\u7ecd\u7684\u76f8\u540c\uff1a\u53ef\u4ee5\u5c06\u8bf7\u6c42\u7ed1\u5b9a\u5230\u7b80\u5355\u7684\u57fa\u5143\uff0c\u4e5f\u53ef\u4ee5\u7ed1\u5b9a\u5230\u590d\u6742\u7684 C# \u5bf9\u8c61\u3002\u552f\u4e00\u7684\u533a\u522b\u662f\u6ca1\u6709\u5177\u6709 [BindProperty] \u5c5e\u6027\u7684 PageModel;\u60a8\u53ea\u80fd\u7ed1\u5b9a\u5230\u4f5c\u65b9\u6cd5\u53c2\u6570\u3002<\/p>\n<p>Listing 20.3 A web API action returning IActionResult to handle error conditions<br \/>\n\u5217\u8868 20.3 \u8fd4\u56de IActionResult \u4ee5\u5904\u7406\u9519\u8bef\u6761\u4ef6\u7684 Web API action<\/p>\n<pre><code>[HttpGet(&quot;fruit\/{id}&quot;)]                #A\npublic ActionResult&lt;string&gt; View(int id)    #B\n{\n    if (id &gt;= 0 &amp;&amp; id &lt; _fruit.Count)   #C\n    {\n        return _fruit[id];    #D\n    }\n    return NotFound();    #E\n}<\/code><\/pre>\n<p>\u2776 Defines the route template for the action method<br \/>\n\u5b9a\u4e49action\u65b9\u6cd5\u7684\u8def\u7531\u6a21\u677f<br \/>\n\u2777 The action method returns an <code>ActionResult&lt;string&gt;<\/code>, so it can return a string or an IActionResult.<br \/>\n\u4f5c\u65b9\u6cd5\u8fd4\u56de  <code>ActionResult&lt;string&gt;<\/code>\uff0c\u56e0\u6b64\u5b83\u53ef\u4ee5\u8fd4\u56de\u5b57\u7b26\u4e32\u6216 IActionResult\u3002<br \/>\n\u2778 An element can be returned only if the id value is a valid _fruit element index.<br \/>\n\u4ec5\u5f53 id \u503c\u662f\u6709\u6548\u7684 _fruit \u5143\u7d20\u7d22\u5f15\u65f6\uff0c\u624d\u80fd\u8fd4\u56de\u5143\u7d20\u3002<br \/>\n\u2779 Returning the data directly returns the data with a 200 status code.<br \/>\n\u8fd4\u56de\u6570\u636e\u76f4\u63a5\u8fd4\u56de\u72b6\u6001\u4ee3\u7801\u4e3a 200 \u7684\u6570\u636e\u3002<br \/>\n\u277a NotFound returns a NotFoundResult, which sends a 404 status code.<br \/>\nNotFound \u8fd4\u56de NotFoundResult\uff0c \uff0c\u8fd9\u4f1a\u53d1\u9001 404 \u72b6\u6001\u4ee3\u7801\u3002<\/p>\n<p>In the successful path for the action method, the id parameter has a value greater than 0 and less than the number of elements in _fruit. When that\u2019s true, the value of the element is returned to the caller. As in listing 20.2, this is achieved by simply returning the data directly, which generates a 200 status code and returns the element in the response body, as shown in figure 20.3. You could also have returned the data using an OkResult, by returning Ok(_fruit[id]), using the Ok helper method on the ControllerBase class; under the hood, the result is identical.<br \/>\n\u5728\u4f5c\u65b9\u6cd5\u7684\u6210\u529f\u8def\u5f84\u4e2d\uff0c id \u53c2\u6570\u7684\u503c\u5927\u4e8e 0 \u4e14\u5c0f\u4e8e _fruit \u4e2d\u7684\u5143\u7d20\u6570\u3002\u5982\u679c\u4e3a true\uff0c\u5219\u5143\u7d20\u7684\u503c\u5c06\u8fd4\u56de\u7ed9\u8c03\u7528\u65b9\u3002\u4e0e\u6e05\u5355 20.2 \u4e00\u6837\uff0c\u8fd9\u662f\u901a\u8fc7\u7b80\u5355\u5730\u76f4\u63a5\u8fd4\u56de\u6570\u636e\u6765\u5b9e\u73b0\u7684\uff0c\u8fd9\u4f1a\u751f\u6210\u4e00\u4e2a 200 \u72b6\u6001\u4ee3\u7801\u5e76\u8fd4\u56de\u54cd\u5e94\u6b63\u6587\u4e2d\u7684\u5143\u7d20\uff0c\u5982\u56fe 20.3 \u6240\u793a\u3002\u60a8\u8fd8\u53ef\u4ee5\u901a\u8fc7 OkResult \u8fd4\u56de Ok\uff08_fruit[id]\uff09\uff0c\u4f7f\u7528 ControllerBase \u7c7b\u4e0a\u7684 Ok \u5e2e\u52a9\u7a0b\u5e8f\u65b9\u6cd5\u8fd4\u56de\u6570\u636e;\u5728\u540e\u53f0\uff0c\u7ed3\u679c\u662f\u76f8\u540c\u7684\u3002<\/p>\n<p><b>NOTE<\/b> Some people get uneasy when they see the phrase helper method, but there\u2019s nothing magic about the ControllerBase helpers; they\u2019re shorthand for creating a new IActionResult of a given type. You don\u2019t have to take my word for it, though. You can always view the source code for the base class on GitHub at <a href=\"http:\/\/mng.bz\/5wQB\">http:\/\/mng.bz\/5wQB<\/a>.<br \/>\n\u6ce8\u610f:\u6709\u4e9b\u4eba\u5728\u770b\u5230\u77ed\u8bed helper method \u65f6\u4f1a\u611f\u5230\u4e0d\u5b89\uff0c\u4f46 ControllerBase helpers \u5e76\u6ca1\u6709\u4ec0\u4e48\u795e\u5947\u4e4b\u5904;\u5b83\u4eec\u662f\u521b\u5efa\u7ed9\u5b9a\u7c7b\u578b\u7684\u65b0 IActionResult \u7684\u7b80\u5199\u3002\u4e0d\u8fc7\uff0c\u4f60\u4e0d\u5fc5\u76f8\u4fe1\u6211\u7684\u8bdd\u3002\u60a8\u59cb\u7ec8\u53ef\u4ee5\u5728 GitHub \u4e0a\u67e5\u770b\u57fa\u7c7b\u7684\u6e90\u4ee3\u7801\uff0c\u7f51\u5740\u4e3a <a href=\"http:\/\/mng.bz\/5wQB\">http:\/\/mng.bz\/5wQB<\/a>\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2003.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.3 Data returned from an action method is serialized into the response body, and it generates a response with status code 200 OK.<br \/>\n\u56fe 20.3 \u4ece\u4f5c\u65b9\u6cd5\u8fd4\u56de\u7684\u6570\u636e\u88ab\u5e8f\u5217\u5316\u5230\u54cd\u5e94\u6b63\u6587\u4e2d\uff0c\u5e76\u751f\u6210\u72b6\u6001\u7801\u4e3a 200 OK \u7684\u54cd\u5e94\u3002<\/p>\n<p>If the id is outside the bounds of the _fruit list, the method calls NotFound() to create a NotFoundResult. When executed, this method generates a 404 Not Found status code response. The [ApiController] attribute automatically converts the response into a standard ProblemDetails instance, as shown in figure 20.4.<br \/>\n\u5982\u679c ID \u8d85\u51fa _fruit \u5217\u8868\u7684\u8fb9\u754c\uff0c\u8be5\u65b9\u6cd5\u5c06\u8c03\u7528 NotFound\uff08\uff09 \u6765\u521b\u5efa NotFoundResult\u3002\u6267\u884c\u65f6\uff0c\u6b64\u65b9\u6cd5\u4f1a\u751f\u6210 404 Not Found \u72b6\u6001\u4ee3\u7801\u54cd\u5e94\u3002[ApiController] \u5c5e\u6027\u4f1a\u81ea\u52a8\u5c06\u54cd\u5e94\u8f6c\u6362\u4e3a\u6807\u51c6 ProblemDetails \u5b9e\u4f8b\uff0c\u5982\u56fe 20.4 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2004.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.4 The [ApiController] attribute converts error responses (in this case a 404 response) into the standard ProblemDetails format.<br \/>\n\u56fe 20.4 [ApiController] \u5c5e\u6027\u5c06\u9519\u8bef\u54cd\u5e94\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a 404 \u54cd\u5e94\uff09\u8f6c\u6362\u4e3a\u6807\u51c6 ProblemDetails \u683c\u5f0f\u3002<\/p>\n<p>One aspect you might find confusing from listing 20.3 is that for the successful case, we return a string, but the method signature of View says we return an <code>ActionResult&lt;string&gt;<\/code>. How is that possible? Why isn\u2019t there a compiler error?<br \/>\n\u6e05\u5355 20.3 \u4e2d\u60a8\u53ef\u80fd\u4f1a\u611f\u5230\u56f0\u60d1\u7684\u4e00\u4e2a\u65b9\u9762\u662f\uff0c\u5bf9\u4e8e\u6210\u529f\u7684\u60c5\u51b5\uff0c\u6211\u4eec\u8fd4\u56de\u4e00\u4e2a\u5b57\u7b26\u4e32\uff0c\u4f46 View \u7684\u65b9\u6cd5\u7b7e\u540d\u8bf4\u6211\u4eec\u8fd4\u56de\u4e00\u4e2a <code>ActionResult&lt;string&gt;<\/code>.\u8fd9\u600e\u4e48\u53ef\u80fd\u5462\uff1f\u4e3a\u4ec0\u4e48\u6ca1\u6709\u7f16\u8bd1\u5668\u9519\u8bef\uff1f<\/p>\n<p>The generic <code>ActionResult&lt;T&gt;<\/code> uses some fancy C# gymnastics with implicit conversions to make this possible. Using <code>ActionResult&lt;T&gt;<\/code> has two benefits:<br \/>\n\u6cdb\u578b<code>ActionResult&lt;T&gt;<\/code>\u4f7f\u7528\u4e00\u4e9b\u5e26\u6709\u9690\u5f0f\u8f6c\u6362\u7684\u82b1\u54e8 C# \u4f53\u6765\u5b9e\u73b0\u8fd9\u4e00\u70b9\u3002\u4f7f\u7528<code>ActionResult&lt;T&gt;<\/code> \u6709\u4e24\u4e2a\u597d\u5904\uff1a<\/p>\n<p>\u2022  You can return either an instance of T or an ActionResult implementation like NotFoundResult from the same method. This can be convenient, as in listing 20.3.<br \/>\n\u60a8\u53ef\u4ee5\u4ece\u540c\u4e00\u65b9\u6cd5\u8fd4\u56de T \u7684\u5b9e\u4f8b\u6216 ActionResult \u5b9e\u73b0\uff08\u5982 NotFoundResult\uff09\u3002\u8fd9\u5f88\u65b9\u4fbf\uff0c\u5982 \u6e05\u5355 20.3 \u6240\u793a\u3002<br \/>\n\u2022  It enables better integration with ASP.NET Core\u2019s OpenAPI support.<br \/>\n\u5b83\u652f\u6301\u66f4\u597d\u5730\u4e0e ASP.NET Core \u7684 OpenAPI \u652f\u6301\u96c6\u6210\u3002<\/p>\n<p>You\u2019re free to return any type of ActionResult from your web API controllers, but you\u2019ll commonly return StatusCodeResult instances, which set the response to a specific status code, with or without associated data. NotFoundResult and OkResult both derive from StatusCodeResult, for example. Another commonly used status code is 400 Bad Request, which is normally returned when the data provided in the request fails validation. You can generate this using a BadRequestResult, but in many cases the [ApiController] attribute can automatically generate 400 responses for you, as you\u2019ll see in section 20.5.<br \/>\n\u60a8\u53ef\u4ee5\u81ea\u7531\u5730\u4ece Web API \u63a7\u5236\u5668\u8fd4\u56de\u4efb\u4f55\u7c7b\u578b\u7684 ActionResult\uff0c\u4f46\u901a\u5e38\u4f1a\u8fd4\u56de StatusCodeResult \u5b9e\u4f8b\uff0c\u8fd9\u4e9b\u5b9e\u4f8b\u5c06\u54cd\u5e94\u8bbe\u7f6e\u4e3a\u7279\u5b9a\u72b6\u6001\u4ee3\u7801\uff0c\u65e0\u8bba\u662f\u5426\u5305\u542b\u5173\u8054\u6570\u636e\u3002\u4f8b\u5982\uff0cNotFoundResult \u548c OkResult \u90fd\u6d3e\u751f\u81ea StatusCodeResult\u3002\u53e6\u4e00\u4e2a\u5e38\u7528\u7684\u72b6\u6001\u4ee3\u7801\u662f 400 Bad Request\uff0c\u901a\u5e38\u5728\u8bf7\u6c42\u4e2d\u63d0\u4f9b\u7684\u6570\u636e\u672a\u901a\u8fc7\u9a8c\u8bc1\u65f6\u8fd4\u56de\u3002\u4f60\u53ef\u4ee5\u4f7f\u7528 BadRequestResult \u6765\u751f\u6210\u5b83\uff0c\u4f46\u5728\u8bb8\u591a\u60c5\u51b5\u4e0b\uff0c [ApiController] \u5c5e\u6027\u53ef\u4ee5\u81ea\u52a8\u751f\u6210 400 \u4e2a\u54cd\u5e94\uff0c\u5982\u7b2c 20.5 \u8282\u6240\u793a\u3002<\/p>\n<p><b>Tip<\/b> You learned about various ActionResults in chapter 15. BadRequestResult, OkResult, and NotFoundResult all inherit from StatusCodeResult and set the appropriate status code for their type (400, 200, and 404, respectively). Using these wrapper classes makes the intention of your code clearer than relying on other developers to understand the significance of the various status code numbers.<br \/>\n\u63d0\u793a\u60a8\u5728\u7b2c 15 \u7ae0\u4e2d\u4e86\u89e3\u4e86\u5404\u79cd ActionResult\u3002BadRequestResult\u3001OkResult \u548c NotFoundResult \u90fd\u7ee7\u627f\u81ea StatusCodeResult\uff0c\u5e76\u4e3a\u5176\u7c7b\u578b\u8bbe\u7f6e\u9002\u5f53\u7684\u72b6\u6001\u4ee3\u7801\uff08\u5206\u522b\u4e3a 400\u3001200 \u548c 404\uff09\u3002\u4f7f\u7528\u8fd9\u4e9b\u5305\u88c5\u7c7b\u53ef\u4ee5\u6bd4\u4f9d\u8d56\u5176\u4ed6\u5f00\u53d1\u4eba\u5458\u6765\u4e86\u89e3\u5404\u79cd\u72b6\u6001\u4ee3\u7801\u7f16\u53f7\u7684\u91cd\u8981\u6027\u66f4\u6e05\u695a\u5730\u4e86\u89e3\u4ee3\u7801\u7684\u610f\u56fe\u3002<\/p>\n<p>Once you\u2019ve returned an ActionResult (or other object) from your controller, it\u2019s serialized to an appropriate response. This works in several ways, depending on<br \/>\n\u4ece\u63a7\u5236\u5668\u8fd4\u56de ActionResult\uff08\u6216\u5176\u4ed6\u5bf9\u8c61\uff09\u540e\uff0c\u5b83\u5c06\u88ab\u5e8f\u5217\u5316\u4e3a\u9002\u5f53\u7684\u54cd\u5e94\u3002\u8fd9\u4ee5\u591a\u79cd\u65b9\u5f0f\u5de5\u4f5c\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e<br \/>\n\u2022  The formatters that your app supports<br \/>\n\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u652f\u6301\u7684\u683c\u5f0f\u5316\u7a0b\u5e8f<br \/>\n\u2022  The data you return from your method<br \/>\n\u60a8\u4ece\u65b9\u6cd5\u8fd4\u56de\u7684\u6570\u636e<br \/>\n\u2022  The data formats the requesting client can handle<br \/>\n\u8bf7\u6c42\u5ba2\u6237\u7aef\u53ef\u4ee5\u5904\u7406\u7684\u6570\u636e\u683c\u5f0f<\/p>\n<p>You\u2019ll learn more about formatters and serializing data in section 20.6, but before we go any further, it\u2019s worth zooming out a little and exploring the parallels between traditional server-side rendered applications and web API endpoints. The two are similar, so it\u2019s important to establish the patterns that they share and where they differ.<br \/>\n\u60a8\u5c06\u5728 Section 20.6 \u4e2d\u4e86\u89e3\u6709\u5173\u683c\u5f0f\u5316\u7a0b\u5e8f\u548c\u5e8f\u5217\u5316\u6570\u636e\u7684\u66f4\u591a\u4fe1\u606f\uff0c\u4f46\u5728\u6211\u4eec\u8fdb\u4e00\u6b65\u8ba8\u8bba\u4e4b\u524d\uff0c\u503c\u5f97\u7a0d\u5fae\u7f29\u5c0f\u5e76\u63a2\u7d22\u4f20\u7edf\u670d\u52a1\u5668\u7aef\u6e32\u67d3\u7684\u5e94\u7528\u7a0b\u5e8f\u548c Web API \u7aef\u70b9\u4e4b\u95f4\u7684\u76f8\u4f3c\u4e4b\u5904\u3002\u8fd9\u4e24\u8005\u76f8\u4f3c\uff0c\u56e0\u6b64\u786e\u5b9a\u5b83\u4eec\u7684\u5171\u540c\u6a21\u5f0f\u548c\u4e0d\u540c\u4e4b\u5904\u975e\u5e38\u91cd\u8981\u3002<\/p>\n<h2>20.2 Applying the MVC design pattern to a web API<\/h2>\n<p>20.2 \u5c06 MVC \u8bbe\u8ba1\u6a21\u5f0f\u5e94\u7528\u4e8e Web API<\/p>\n<p>In ASP.NET Core, the same underlying framework is used in conjunction with web API controllers, Razor Pages, and MVC controllers with views. You\u2019ve already seen this yourself; the web API FruitController you created in section 20.2 looks similar to the MVC controllers you saw in chapter 19.<br \/>\n\u5728 ASP.NET Core \u4e2d\uff0c\u76f8\u540c\u7684\u57fa\u7840\u6846\u67b6\u4e0e Web API \u63a7\u5236\u5668\u3001Razor Pages \u548c\u5177\u6709\u89c6\u56fe\u7684 MVC \u63a7\u5236\u5668\u7ed3\u5408\u4f7f\u7528\u3002\u4f60\u81ea\u5df1\u5df2\u7ecf\u89c1\u8fc7\u4e86;\u60a8\u5728\u7b2c 20.2 \u8282\u4e2d\u521b\u5efa\u7684 Web API FruitController \u770b\u8d77\u6765\u7c7b\u4f3c\u4e8e\u60a8\u5728\u7b2c 19 \u7ae0\u4e2d\u770b\u5230\u7684 MVC \u63a7\u5236\u5668\u3002<\/p>\n<p>Consequently, even if you\u2019re building an application that consists entirely of web APIs, using no server-side rendering of HTML, the MVC design pattern still applies. Whether you\u2019re building traditional web applications or web APIs, you can structure your application virtually identically.<br \/>\n\u56e0\u6b64\uff0c\u5373\u4f7f\u60a8\u6b63\u5728\u6784\u5efa\u4e00\u4e2a\u5b8c\u5168\u7531 Web API \u7ec4\u6210\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u4e0d\u4f7f\u7528\u670d\u52a1\u5668\u7aef\u7684 HTML \u5448\u73b0\uff0cMVC \u8bbe\u8ba1\u6a21\u5f0f\u4ecd\u7136\u9002\u7528\u3002\u65e0\u8bba\u60a8\u662f\u6784\u5efa\u4f20\u7edf\u7684 Web \u5e94\u7528\u7a0b\u5e8f\u8fd8\u662f Web API\uff0c\u60a8\u90fd\u53ef\u4ee5\u4ee5\u51e0\u4e4e\u76f8\u540c\u7684\u65b9\u5f0f\u6784\u5efa\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>By now I hope you\u2019re nicely familiar with how ASP.NET Core handles a request. But in case you\u2019re not, figure 20.5 shows how the framework handles a typical Razor Pages request after it passes through the middleware pipeline. This example shows how a request to view the available fruit on a traditional grocery store website might look.<br \/>\n\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u5e0c\u671b\u60a8\u5df2\u7ecf\u975e\u5e38\u719f\u6089 ASP.NET Core \u5982\u4f55\u5904\u7406\u8bf7\u6c42\u3002\u4f46\u5982\u679c\u4f60\u4e0d\u662f\uff0c\u56fe 20.5 \u663e\u793a\u4e86\u6846\u67b6\u5728\u5178\u578b\u7684 Razor Pages \u8bf7\u6c42\u901a\u8fc7\u4e2d\u95f4\u4ef6\u7ba1\u9053\u540e\u5982\u4f55\u5904\u7406\u8be5\u8bf7\u6c42\u3002\u6b64\u793a\u4f8b\u663e\u793a\u4e86\u5728\u4f20\u7edf\u6742\u8d27\u5e97\u7f51\u7ad9\u4e0a\u67e5\u770b\u53ef\u7528\u6c34\u679c\u7684\u8bf7\u6c42\u53ef\u80fd\u662f\u4ec0\u4e48\u6837\u5b50\u7684\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2005.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.5 Handling a request to a traditional Razor Pages application, in which the view generates an HTML response that\u2019s sent back to the user. This diagram should be familiar by now!<br \/>\n\u56fe 20.5 \u5904\u7406\u5bf9\u4f20\u7edf Razor Pages \u5e94\u7528\u7a0b\u5e8f\u7684\u8bf7\u6c42\uff0c\u5176\u4e2d\u89c6\u56fe\u751f\u6210 HTML \u54cd\u5e94\uff0c\u8be5\u54cd\u5e94\u5c06\u53d1\u9001\u56de\u7ed9\u7528\u6237\u3002\u8fd9\u5f20\u56fe\u73b0\u5728\u5e94\u8be5\u5f88\u719f\u6089\u4e86\uff01<\/p>\n<p>The RoutingMiddleware routes the request to view all the fruit listed in the apples category to the Fruit.cshtml Razor Page. The EndpointMiddleware then constructs a binding model, validates it, sets it as a property on the Razor Page\u2019s PageModel, and sets the ModelState property on the PageModel base class with details of any validation errors. The page handler interacts with the application model by calling into services, talking to a database, and fetching any necessary data.<br \/>\nRoutingMiddleware \u5c06\u8bf7\u6c42\u8def\u7531\u5230 Fruit.cshtml Razor \u9875\u9762\uff0c\u4ee5\u67e5\u770b apples \u7c7b\u522b\u4e2d\u5217\u51fa\u7684\u6240\u6709\u6c34\u679c\u3002\u7136\u540e\uff0cEndpointMiddleware \u6784\u9020\u4e00\u4e2a\u7ed1\u5b9a\u6a21\u578b\uff0c\u5bf9\u5176\u8fdb\u884c\u9a8c\u8bc1\uff0c\u5c06\u5176\u8bbe\u7f6e\u4e3a Razor \u9875\u9762\u7684 PageModel \u4e0a\u7684\u5c5e\u6027\uff0c\u5e76\u5728 PageModel \u57fa\u7c7b\u4e0a\u8bbe\u7f6e ModelState \u5c5e\u6027\uff0c\u5176\u4e2d\u5305\u542b\u4efb\u4f55\u9a8c\u8bc1\u9519\u8bef\u7684\u8be6\u7ec6\u4fe1\u606f\u3002\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u901a\u8fc7\u8c03\u7528\u670d\u52a1\u3001\u4e0e\u6570\u636e\u5e93\u901a\u4fe1\u4ee5\u53ca\u83b7\u53d6\u4efb\u4f55\u5fc5\u8981\u7684\u6570\u636e\u6765\u4e0e\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u8fdb\u884c\u4ea4\u4e92\u3002<\/p>\n<p>Finally, the Razor Page executes its Razor view using the PageModel to generate the HTML response. The response returns through the middleware pipeline and out to the user\u2019s browser.<br \/>\n\u6700\u540e\uff0cRazor \u9875\u9762\u4f7f\u7528 PageModel \u6267\u884c\u5176 Razor \u89c6\u56fe\u4ee5\u751f\u6210 HTML \u54cd\u5e94\u3002\u54cd\u5e94\u901a\u8fc7\u4e2d\u95f4\u4ef6\u7ba1\u9053\u8fd4\u56de\u5e76\u8f93\u51fa\u5230\u7528\u6237\u7684\u6d4f\u89c8\u5668\u3002<\/p>\n<p>How would this change if the request came from a client-side or mobile application? If you want to serve machine-readable JSON instead of HTML, what is different for web API controllers? As shown in figure 20.6, the answer is \u201cvery little.\u201d The main changes are related to switching from Razor Pages to controllers and actions, but as you saw in chapter 19, both approaches use the same general paradigms.<br \/>\n\u5982\u679c\u8bf7\u6c42\u6765\u81ea\u5ba2\u6237\u7aef\u6216\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\uff0c\u60c5\u51b5\u4f1a\u5982\u4f55\u53d8\u5316\uff1f\u5982\u679c\u8981\u63d0\u4f9b\u673a\u5668\u53ef\u8bfb\u7684 JSON \u800c\u4e0d\u662f HTML\uff0c\u90a3\u4e48 Web API \u63a7\u5236\u5668\u6709\u4ec0\u4e48\u4e0d\u540c\uff1f\u5982\u56fe 20.6 \u6240\u793a\uff0c\u7b54\u6848\u662f \u201cvery little\u201d\u3002\u4e3b\u8981\u66f4\u6539\u4e0e\u4ece Razor Pages \u5207\u6362\u5230\u63a7\u5236\u5668\u548c\u4f5c\u6709\u5173\uff0c\u4f46\u6b63\u5982\u60a8\u5728\u7b2c 19 \u7ae0\u4e2d\u770b\u5230\u7684\u90a3\u6837\uff0c\u8fd9\u4e24\u79cd\u65b9\u6cd5\u90fd\u4f7f\u7528\u76f8\u540c\u7684\u901a\u7528\u8303\u4f8b\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2006.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.6 A call to a web API endpoint in an e-commerce ASP.NET Core web application. The ghosted portion of the diagram is identical to figure 20.5.<br \/>\n\u56fe 20.6 \u5bf9\u7535\u5b50\u5546\u52a1 ASP.NET Core Web \u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 Web API \u7ec8\u7aef\u8282\u70b9\u7684\u8c03\u7528\u3002\u8be5\u56fe\u7684\u91cd\u5f71\u90e8\u5206\u4e0e\u56fe 20.5 \u76f8\u540c\u3002<\/p>\n<p>As before, the routing middleware selects an endpoint to invoke based on the incoming URL. For API controllers this is a controller and action instead of a Razor Page.<br \/>\n\u4e0e\u4ee5\u524d\u4e00\u6837\uff0c\u8def\u7531\u4e2d\u95f4\u4ef6\u6839\u636e\u4f20\u5165 URL \u9009\u62e9\u8981\u8c03\u7528\u7684\u7ec8\u7aef\u8282\u70b9\u3002\u5bf9\u4e8e API \u63a7\u5236\u5668\uff0c\u8fd9\u662f\u63a7\u5236\u5668\u548c\u4f5c\uff0c\u800c\u4e0d\u662f Razor \u9875\u9762\u3002<\/p>\n<p>After routing comes model-binding, in which the binder creates a binding model and populates it with values from the request. web API controllers often accept data in more formats than Razor Pages, such as XML, but otherwise the model-binding process is the same as for the Razor Pages request. Validation also occurs in the same way, and the ModelState property on the ControllerBase base class is populated with any validation errors.<br \/>\n\u8def\u7531\u4e4b\u540e\u662f\u6a21\u578b\u7ed1\u5b9a\uff0c\u5176\u4e2d Binder \u521b\u5efa\u4e00\u4e2a\u7ed1\u5b9a\u6a21\u578b\uff0c\u5e76\u4f7f\u7528\u8bf7\u6c42\u4e2d\u7684\u503c\u586b\u5145\u5b83\u3002Web API \u63a7\u5236\u5668\u901a\u5e38\u63a5\u53d7\u6bd4 Razor Pages \u66f4\u591a\u683c\u5f0f\u7684\u6570\u636e\uff08\u4f8b\u5982 XML\uff09\uff0c\u4f46\u5176\u4ed6\u65b9\u9762\uff0c\u6a21\u578b\u7ed1\u5b9a\u8fc7\u7a0b\u4e0e Razor Pages \u8bf7\u6c42\u76f8\u540c\u3002\u9a8c\u8bc1\u4e5f\u4ee5\u76f8\u540c\u7684\u65b9\u5f0f\u8fdb\u884c\uff0c\u5e76\u4e14 ControllerBase \u57fa\u7c7b\u4e0a\u7684 ModelState \u5c5e\u6027\u4e2d\u586b\u5145\u4e86\u4efb\u4f55\u9a8c\u8bc1\u9519\u8bef\u3002<\/p>\n<p><b>NOTE<\/b> Web APIs use input formatters to accept data sent to them in a variety of formats. Commonly these formats are JSON or XML, but you can create input formatters for any sort of type, such as CSV. I show how to enable the XML input formatter in section 20.6. You can see how to create a custom input formatter at <a href=\"http:\/\/mng.bz\/e5gG\">http:\/\/mng.bz\/e5gG<\/a>.<br \/>\n\u6ce8\u610f:Web API \u4f7f\u7528\u8f93\u5165\u683c\u5f0f\u5316\u7a0b\u5e8f\u6765\u63a5\u53d7\u4ee5\u5404\u79cd\u683c\u5f0f\u53d1\u9001\u7ed9\u5b83\u4eec\u7684\u6570\u636e\u3002\u8fd9\u4e9b\u683c\u5f0f\u901a\u5e38\u4e3a JSON \u6216 XML\uff0c\u4f46\u60a8\u53ef\u4ee5\u4e3a\u4efb\u4f55\u7c7b\u578b\u7684\u8f93\u5165\u683c\u5f0f\u5316\u7a0b\u5e8f\u521b\u5efa\uff0c\u4f8b\u5982 CSV\u3002\u6211\u5728 Section 20.6 \u4e2d\u5c55\u793a\u4e86\u5982\u4f55\u542f\u7528 XML input \u683c\u5f0f\u5316\u7a0b\u5e8f\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/e5gG\">http:\/\/mng.bz\/e5gG<\/a> \u4e2d\u4e86\u89e3\u5982\u4f55\u521b\u5efa\u81ea\u5b9a\u4e49\u8f93\u5165\u683c\u5f0f\u5316\u7a0b\u5e8f\u3002<\/p>\n<p>The action method is the equivalent of the Razor Page handler; it interacts with the application model in the same way. This is an important point; by separating the behavior of your app into an application model instead of incorporating it into your pages and controllers themselves, you\u2019re able to reuse the business logic of your application with multiple UI paradigms.<br \/>\naction\u65b9\u6cd5\u7b49\u6548\u4e8e Razor Page \u5904\u7406\u7a0b\u5e8f;\u5b83\u4ee5\u76f8\u540c\u7684\u65b9\u5f0f\u4e0e\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u4ea4\u4e92\u3002\u8fd9\u662f\u5f88\u91cd\u8981\u7684\u4e00\u70b9;\u901a\u8fc7\u5c06\u5e94\u7528\u7a0b\u5e8f\u7684\u884c\u4e3a\u5206\u79bb\u5230\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u4e2d\uff0c\u800c\u4e0d\u662f\u5c06\u5176\u5408\u5e76\u5230\u9875\u9762\u548c\u63a7\u5236\u5668\u672c\u8eab\u4e2d\uff0c\u60a8\u53ef\u4ee5\u5229\u7528\u591a\u4e2a UI \u8303\u4f8b\u91cd\u7528\u5e94\u7528\u7a0b\u5e8f\u7684\u4e1a\u52a1\u903b\u8f91\u3002<\/p>\n<p><b>Tip<\/b> Where possible, keep your page handlers and controllers as simple as practicable. Move all your business logic decisions into the services that make up your application model, and keep your Razor Pages and API controllers focused on the mechanics of interacting with a user or client.<br \/>\n\u63d0\u793a:\u5728\u53ef\u80fd\u7684\u60c5\u51b5\u4e0b\uff0c\u8bf7\u5c3d\u53ef\u80fd\u4fdd\u6301\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u548c\u63a7\u5236\u5668\u7684\u7b80\u5355\u6027\u3002\u5c06\u6240\u6709\u4e1a\u52a1\u903b\u8f91\u51b3\u7b56\u79fb\u52a8\u5230\u6784\u6210\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u7684\u670d\u52a1\u4e2d\uff0c\u5e76\u4f7f Razor Pages \u548c API \u63a7\u5236\u5668\u4e13\u6ce8\u4e8e\u4e0e\u7528\u6237\u6216\u5ba2\u6237\u7aef\u4ea4\u4e92\u7684\u673a\u5236\u3002<\/p>\n<p>After the application model has returned the data necessary to service the request\u2014the fruit objects in the apples category\u2014you see the first significant difference between API controllers and Razor Pages. Instead of adding values to the PageModel to be used in a Razor view, the action method creates an API model. This is analogous to the PageModel, but rather than containing data used to generate an HTML view, it contains the data that will be sent back in the response.<br \/>\n\u5728\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u8fd4\u56de\u4e3a\u8bf7\u6c42\u63d0\u4f9b\u670d\u52a1\u6240\u9700\u7684\u6570\u636e\uff08apples \u7c7b\u522b\u4e2d\u7684 fruit \u5bf9\u8c61\uff09\u540e\uff0c\u60a8\u4f1a\u770b\u5230 API \u63a7\u5236\u5668\u548c Razor Pages \u4e4b\u95f4\u7684\u7b2c\u4e00\u4e2a\u663e\u8457\u5dee\u5f02\u3002\u4f5c\u65b9\u6cd5\u4e0d\u662f\u5411 PageModel \u6dfb\u52a0\u503c\u4ee5\u5728 Razor \u89c6\u56fe\u4e2d\u4f7f\u7528\u7684\uff0c\u800c\u662f\u521b\u5efa\u4e00\u4e2a API \u6a21\u578b\u3002\u8fd9\u7c7b\u4f3c\u4e8e PageModel\uff0c\u4f46\u5b83\u4e0d\u5305\u542b\u7528\u4e8e\u751f\u6210 HTML \u89c6\u56fe\u7684\u6570\u636e\uff0c\u800c\u662f\u5305\u542b\u5c06\u5728\u54cd\u5e94\u4e2d\u53d1\u56de\u7684\u6570\u636e\u3002<\/p>\n<p><b>DEFINITION<\/b> View models and PageModels contain both the data required to build a response and metadata about how to build the response. API models typically contain only the data to be returned in the response.<br \/>\n\u5b9a\u4e49:\u89c6\u56fe\u6a21\u578b\u548c PageModel \u5305\u542b\u6784\u5efa\u54cd\u5e94\u6240\u9700\u7684\u6570\u636e\u4ee5\u53ca\u6709\u5173\u5982\u4f55\u6784\u5efa\u54cd\u5e94\u7684\u5143\u6570\u636e\u3002API \u6a21\u578b\u901a\u5e38\u4ec5\u5305\u542b\u8981\u5728\u54cd\u5e94\u4e2d\u8fd4\u56de\u7684\u6570\u636e\u3002<\/p>\n<p>When we looked at the Razor Pages app, we used the PageModel in conjunction with a Razor view template to build the final response. With the web API app, we use the API model in conjunction with an output formatter. An output formatter, as the name suggests, serializes the API model into a machine-readable response, such as JSON or XML. The output formatter forms the V in the web API version of MVC by choosing an appropriate representation of the data to return.<br \/>\n\u5f53\u6211\u4eec\u67e5\u770b Razor Pages \u5e94\u7528\u65f6\uff0c\u6211\u4eec\u5c06 PageModel \u4e0e Razor \u89c6\u56fe\u6a21\u677f\u7ed3\u5408\u4f7f\u7528\u6765\u6784\u5efa\u6700\u7ec8\u54cd\u5e94\u3002\u5bf9\u4e8e Web API \u5e94\u7528\u7a0b\u5e8f\uff0c\u6211\u4eec\u5c06 API \u6a21\u578b\u4e0e\u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f\u7ed3\u5408\u4f7f\u7528\u3002\u987e\u540d\u601d\u4e49\uff0c\u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f\u5c06 API \u6a21\u578b\u5e8f\u5217\u5316\u4e3a\u673a\u5668\u53ef\u8bfb\u7684\u54cd\u5e94\uff0c\u4f8b\u5982 JSON \u6216 XML\u3002\u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f\u901a\u8fc7\u9009\u62e9\u8981\u8fd4\u56de\u7684\u6570\u636e\u7684\u9002\u5f53\u8868\u793a\u5f62\u5f0f\uff0c\u5728 MVC \u7684 Web API \u7248\u672c\u4e2d\u5f62\u6210 V\u3002<\/p>\n<p>Finally, as for the Razor Pages app, the generated response is sent back through the middleware pipeline, passing through each of the configured middleware components, and back to the original caller.<br \/>\n\u6700\u540e\uff0c\u5bf9\u4e8e Razor Pages \u5e94\u7528\uff0c\u751f\u6210\u7684\u54cd\u5e94\u901a\u8fc7\u4e2d\u95f4\u4ef6\u7ba1\u9053\u53d1\u9001\u56de\uff0c\u901a\u8fc7\u6bcf\u4e2a\u914d\u7f6e\u7684\u4e2d\u95f4\u4ef6\u7ec4\u4ef6\uff0c\u5e76\u8fd4\u56de\u7ed9\u539f\u59cb\u8c03\u7528\u65b9\u3002<\/p>\n<p>I hope the parallels between Razor Pages and web APIs are clear. The majority of the behavior is identical; only the response varies. Everything from when the request arrives to the interaction with the application model is similar between the paradigms.<br \/>\n\u6211\u5e0c\u671b Razor Pages \u548c Web API \u4e4b\u95f4\u7684\u76f8\u4f3c\u4e4b\u5904\u662f\u660e\u786e\u7684\u3002\u5927\u591a\u6570\u884c\u4e3a\u662f\u76f8\u540c\u7684;\u53ea\u662f\u53cd\u5e94\u4e0d\u540c\u3002\u4ece\u8bf7\u6c42\u5230\u8fbe\u5230\u4e0e\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u7684\u4ea4\u4e92\uff0c\u8303\u4f8b\u4e4b\u95f4\u7684\u4e00\u5207\u90fd\u662f\u76f8\u4f3c\u7684\u3002<\/p>\n<p>Most of the differences between Razor Pages and web APIs have less to do with the way the framework works under the hood and are instead related to how the different paradigms are used. For example, in the next section you\u2019ll learn how the routing constructs you learned about in chapters 6 and 15 are used with web APIs, using attribute routing.<br \/>\nRazor Pages \u548c Web API \u4e4b\u95f4\u7684\u5927\u591a\u6570\u5dee\u5f02\u4e0e\u6846\u67b6\u5728\u540e\u53f0\u7684\u5de5\u4f5c\u65b9\u5f0f\u5173\u7cfb\u4e0d\u5927\uff0c\u800c\u662f\u4e0e\u4e0d\u540c\u8303\u4f8b\u7684\u4f7f\u7528\u65b9\u5f0f\u6709\u5173\u3002\u4f8b\u5982\uff0c\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528\u5c5e\u6027\u8def\u7531\u5c06\u60a8\u5728\u7b2c 6 \u7ae0\u548c\u7b2c 15 \u7ae0\u4e2d\u5b66\u5230\u7684\u8def\u7531\u7ed3\u6784\u4e0e Web API \u4e00\u8d77\u4f7f\u7528\u3002<\/p>\n<h2>20.3 Attribute routing: Linking action methods to URLs<\/h2>\n<p>20.3 \u5c5e\u6027\u8def\u7531\uff1a\u5c06\u4f5c\u65b9\u6cd5\u94fe\u63a5\u5230 URL<\/p>\n<p>In this section you\u2019ll learn about attribute routing: the mechanism for associating web API controller actions with a given route template. You\u2019ll see how to associate controller actions with specific HTTP verbs like GET and POST and how to avoid duplication in your templates.<br \/>\n\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5c5e\u6027\u8def\u7531\uff1a\u5c06 Web API \u63a7\u5236\u5668\u4f5c\u4e0e\u7ed9\u5b9a\u8def\u7531\u6a21\u677f\u5173\u8054\u7684\u673a\u5236\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u5c06\u63a7\u5236\u5668\u4f5c\u4e0e\u7279\u5b9a\u7684 HTTP \u52a8\u8bcd\uff08\u5982 GET \u548c POST\uff09\u76f8\u5173\u8054\uff0c\u4ee5\u53ca\u5982\u4f55\u907f\u514d\u6a21\u677f\u4e2d\u7684\u91cd\u590d\u3002<\/p>\n<p>We covered route templates in depth in chapter 6 in the context of minimal APIs, and again in chapter 14 with Razor Pages, and you\u2019ll be pleased to know that you use exactly the same route templates with API controllers. The only difference is how you specify the templates. With Razor Pages you use the @page directive, and with minimal APIs you use MapGet() or MapPost(), whereas with API controllers you use routing attributes.<br \/>\n\u6211\u4eec\u5728\u7b2c 6 \u7ae0\u4e2d\u6df1\u5165\u4ecb\u7ecd\u4e86\u6700\u5c0f API \u7684\u8def\u7531\u6a21\u677f\uff0c\u5e76\u5728\u7b2c 14 \u7ae0\u4e2d\u518d\u6b21\u4ecb\u7ecd\u4e86 Razor Pages\uff0c\u60a8\u4f1a\u5f88\u9ad8\u5174\u5730\u77e5\u9053\u60a8\u5bf9 API \u63a7\u5236\u5668\u4f7f\u7528\u5b8c\u5168\u76f8\u540c\u7684\u8def\u7531\u6a21\u677f\u3002\u552f\u4e00\u7684\u533a\u522b\u662f\u6307\u5b9a\u6a21\u677f\u7684\u65b9\u5f0f\u3002\u5bf9\u4e8e Razor Pages\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 @page \u6307\u4ee4\uff0c\u5bf9\u4e8e\u6700\u5c11\u7684 API\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 MapGet\uff08\uff09 \u6216 MapPost\uff08\uff09\uff0c\u800c\u5bf9\u4e8e API \u63a7\u5236\u5668\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u5c5e\u6027\u3002<\/p>\n<p><b>NOTE<\/b> All three paradigms use explicit routing under the hood. The alternative, conventional routing, is typically used with traditional MVC controllers and views, as described in chapter 19. As I\u2019ve mentioned, I don\u2019t recommend using that approach generally, so I don\u2019t cover conventional routing in this book.<br \/>\n\u6ce8\u610f:\u8fd9\u4e09\u79cd\u8303\u4f8b\u90fd\u5728\u540e\u53f0\u4f7f\u7528\u663e\u5f0f\u8def\u7531\u3002\u53e6\u4e00\u79cd\u9009\u62e9\uff0c\u5373\u4f20\u7edf\u8def\u7531\uff0c\u901a\u5e38\u4e0e\u4f20\u7edf\u7684 MVC \u63a7\u5236\u5668\u548c\u89c6\u56fe\u4e00\u8d77\u4f7f\u7528\uff0c\u5982\u7b2c 19 \u7ae0\u6240\u8ff0\u3002\u6b63\u5982\u6211\u6240\u63d0\u5230\u7684\uff0c\u6211\u4e0d\u5efa\u8bae\u901a\u5e38\u4f7f\u7528\u8fd9\u79cd\u65b9\u6cd5\uff0c\u56e0\u6b64\u6211\u5728\u672c\u4e66\u4e2d\u4e0d\u4ecb\u7ecd\u4f20\u7edf\u8def\u7531\u3002<\/p>\n<p>With attribute routing, you decorate each action method in an API controller with an attribute and provide the associated route template for the action method, as shown in the following listing.<br \/>\n\u4f7f\u7528\u5c5e\u6027\u8def\u7531\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u5c5e\u6027\u4fee\u9970 API \u63a7\u5236\u5668\u4e2d\u7684\u6bcf\u4e2a\u4f5c\u65b9\u6cd5\uff0c\u5e76\u4e3a\u4f5c\u65b9\u6cd5\u63d0\u4f9b\u5173\u8054\u7684\u8def\u7531\u6a21\u677f\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 20.4 Attribute routing example<br \/>\n\u5217\u8868 20.4 \u5c5e\u6027\u8def\u7531\u793a\u4f8b<\/p>\n<pre><code>public class HomeController: Controller\n{\n    [Route(&quot;&quot;)]                  #A\n    public IActionResult Index()\n    {\n         \/* method implementation*\/\n    }\n\n    [Route(&quot;contact&quot;)]             #B\n    public IActionResult Contact()\n    {\n         \/* method implementation*\/\n    }\n}<\/code><\/pre>\n<p>\u2776 The Index action will be executed when the \/ URL is requested.<br \/>\n\u8bf7\u6c42 \/ URL \u65f6\uff0c\u5c06\u6267\u884c Index action\u3002<br \/>\n\u2777 The Contact action will be executed when the \/contact URL is requested.<br \/>\n\u8bf7\u6c42 \/contact URL \u65f6\uff0c\u5c06\u6267\u884c Contact action\u3002<\/p>\n<p>Each [Route] attribute defines a route template that should be associated with the action method. In the example provided, the \/ URL maps directly to the Index method and the \/contact URL maps to the Contact method.<br \/>\n\u6bcf\u4e2a [Route] \u5c5e\u6027\u90fd\u5b9a\u4e49\u4e00\u4e2a\u5e94\u4e0e\u4f5c\u65b9\u6cd5\u5173\u8054\u7684\u8def\u7531\u6a21\u677f\u3002\u5728\u63d0\u4f9b\u7684\u793a\u4f8b\u4e2d\uff0c\/ URL \u76f4\u63a5\u6620\u5c04\u5230 Index \u65b9\u6cd5\uff0c\u800c \/contact URL \u6620\u5c04\u5230 Contact \u65b9\u6cd5\u3002<\/p>\n<p>Attribute routing maps URLs to a specific action method, but a single action method can still have multiple route templates and hence can correspond to multiple URLs. Each template must be declared with its own RouteAttribute, as shown in this listing, which shows the skeleton of a web API for a car-racing game.<br \/>\n\u5c5e\u6027\u8def\u7531\u5c06 URL \u6620\u5c04\u5230\u7279\u5b9a\u7684\u4f5c\u65b9\u6cd5\uff0c\u4f46\u5355\u4e2a\u4f5c\u65b9\u6cd5\u4ecd\u53ef\u4ee5\u5177\u6709\u591a\u4e2a\u8def\u7531\u6a21\u677f\uff0c\u56e0\u6b64\u53ef\u4ee5\u5bf9\u5e94\u4e8e\u591a\u4e2a URL\u3002\u6bcf\u4e2a\u6a21\u677f\u90fd\u5fc5\u987b\u4f7f\u7528\u81ea\u5df1\u7684 RouteAttribute \u8fdb\u884c\u58f0\u660e\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\uff0c\u5b83\u663e\u793a\u4e86\u4e00\u4e2a\u8d5b\u8f66\u6e38\u620f\u7684 Web API \u7684\u6846\u67b6\u3002<\/p>\n<p>Listing 20.5 Attribute routing with multiple attributes<br \/>\n\u793a\u4f8b 20.5 \u5177\u6709\u591a\u4e2a\u5c5e\u6027\u7684\u5c5e\u6027\u8def\u7531<\/p>\n<pre><code>public class CarController\n{\n    [Route(&quot;car\/start&quot;)]         #A\n    [Route(&quot;car\/ignition&quot;)]      #A\n    [Route(&quot;start-car&quot;)]         #A\n    public IActionResult Start()    #B\n    {\n         \/* method implementation*\/\n    }\n\n    [Route(&quot;car\/speed\/{speed}&quot;)]             #C\n    [Route(&quot;set-speed\/{speed}&quot;)]             #C\n    public IActionResult SetCarSpeed(int speed)  \n    {\n         \/* method implementation*\/\n    }\n}<\/code><\/pre>\n<p>\u2776 The Start method will be executed when any of these route templates is matched.<br \/>\n\u5f53\u8fd9\u4e9b\u8def\u7531\u6a21\u677f\u4e2d\u7684\u4efb\u4f55\u4e00\u4e2a\u5339\u914d\u65f6\uff0c\u5c06\u6267\u884c Start \u65b9\u6cd5\u3002<br \/>\n\u2777 The name of the action method has no effect on the route template.<br \/>\naction\u65b9\u6cd5\u7684\u540d\u79f0\u5bf9\u8def\u7531\u6a21\u677f\u6ca1\u6709\u5f71\u54cd\u3002<br \/>\n\u2778 The RouteAttribute template can contain route parameters, in this case {speed}.<br \/>\nRouteAttribute \u6a21\u677f\u53ef\u4ee5\u5305\u542b\u8def\u7531\u53c2\u6570\uff0c\u5728\u672c\u4f8b\u4e2d\u4e3a {speed}\u3002<\/p>\n<p>The listing shows two different action methods, both of which can be accessed from multiple URLs. For example, the Start method will be executed when any of the following URLs is requested:<br \/>\n\u8be5\u5217\u8868\u663e\u793a\u4e86\u4e24\u79cd\u4e0d\u540c\u7684\u4f5c\u65b9\u6cd5\uff0c\u8fd9\u4e24\u79cd\u65b9\u6cd5\u90fd\u53ef\u4ee5\u4ece\u591a\u4e2a URL \u8bbf\u95ee\u3002\u4f8b\u5982\uff0c\u5f53\u8bf7\u6c42\u4ee5\u4e0b\u4efb\u4e00 URL \u65f6\uff0c\u5c06\u6267\u884c Start \u65b9\u6cd5\uff1a<\/p>\n<pre><code>\/car\/start\n\/car\/ignition\n\/start-car<\/code><\/pre>\n<p>These URLs are completely independent of the controller and action method names; only the value in the RouteAttribute matters.<br \/>\n\u8fd9\u4e9b URL \u5b8c\u5168\u72ec\u7acb\u4e8e\u63a7\u5236\u5668\u548c\u4f5c\u65b9\u6cd5\u540d\u79f0;\u53ea\u6709 RouteAttribute \u4e2d\u7684\u503c\u624d\u91cd\u8981\u3002<\/p>\n<p><b>NOTE<\/b> By default, the controller and action name have no bearing on the URLs or route templates when RouteAttributes are used.<br \/>\n\u6ce8\u610f:\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u5f53\u4f7f\u7528 RouteAttributes \u65f6\uff0c\u63a7\u5236\u5668\u548c\u4f5c\u540d\u79f0\u4e0e URL \u6216\u8def\u7531\u6a21\u677f\u65e0\u5173\u3002<\/p>\n<p>The templates used in route attributes are standard route templates, the same as you used in chapter 6. You can use literal segments, and you\u2019re free to define route parameters that will extract values from the URL, as shown by the SetCarSpeed method in listing 20.5. That method defines two route templates, both of which define a route parameter, {speed}.<br \/>\n\u8def\u7531\u5c5e\u6027\u4e2d\u4f7f\u7528\u7684\u6a21\u677f\u662f\u6807\u51c6\u8def\u7531\u6a21\u677f\uff0c\u4e0e\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u4f7f\u7528\u7684\u6a21\u677f\u76f8\u540c\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u6587\u5b57\u6bb5\uff0c\u5e76\u4e14\u53ef\u4ee5\u81ea\u7531\u5b9a\u4e49\u5c06\u4ece URL \u4e2d\u63d0\u53d6\u503c\u7684\u8def\u7531\u53c2\u6570\uff0c\u5982\u6e05\u5355 20.5 \u4e2d\u7684 SetCarSpeed \u65b9\u6cd5\u6240\u793a\u3002\u8be5\u65b9\u6cd5\u5b9a\u4e49\u4e86\u4e24\u4e2a\u8def\u7531\u6a21\u677f\uff0c\u8fd9\u4e24\u4e2a\u6a21\u677f\u90fd\u5b9a\u4e49\u4e86\u4e00\u4e2a\u8def\u7531\u53c2\u6570 {speed}\u3002<\/p>\n<p><b>Tip<\/b> I\u2019ve used multiple [Route] attributes on each action in this example, but it\u2019s best practice to expose your action at a single URL. This will make your API easier to understand and for other applications to consume.<br \/>\n\u63d0\u793a:\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u6211\u5bf9\u6bcf\u4e2a\u4f5c\u4f7f\u7528\u4e86\u591a\u4e2a [Route] \u5c5e\u6027\uff0c\u4f46\u6700\u4f73\u505a\u6cd5\u662f\u5728\u5355\u4e2a URL \u4e0a\u516c\u5f00\u60a8\u7684\u4f5c\u3002\u8fd9\u5c06\u4f7f\u60a8\u7684 API \u66f4\u6613\u4e8e\u7406\u89e3\uff0c\u5e76\u53ef\u4f9b\u5176\u4ed6\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528\u3002<\/p>\n<p>As in all parts of ASP.NET Core, route parameters represent a segment of the URL that can vary. As with minimal APIs, and Razor Pages, the route parameters in your RouteAttribute templates can<br \/>\n\u4e0e ASP.NET Core \u7684\u6240\u6709\u90e8\u5206\u4e00\u6837\uff0c\u8def\u7531\u53c2\u6570\u8868\u793a URL \u4e2d\u53ef\u4ee5\u53d8\u5316\u7684\u4e00\u6bb5\u3002\u4e0e\u6700\u5c11\u7684 API \u548c Razor Pages \u4e00\u6837\uff0cRouteAttribute \u6a21\u677f\u4e2d\u7684\u8def\u7531\u53c2\u6570\u53ef\u4ee5<\/p>\n<p>\u2022  Be optional<br \/>\n\u2022  Have default values<br \/>\n\u2022  Use route constraints<\/p>\n<p>For example, you could update the SetCarSpeed method in the previous listing to constrain {speed} to an integer and to default to 20 like so:<br \/>\n\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u66f4\u65b0\u4e0a\u4e00\u4e2a\u6e05\u5355\u4e2d\u7684 SetCarSpeed \u65b9\u6cd5\uff0c\u5c06 {speed} \u7ea6\u675f\u4e3a\u6574\u6570\uff0c\u5e76\u9ed8\u8ba4\u4e3a 20\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<pre><code>[Route(&quot;car\/speed\/{speed=20:int}&quot;)]\n[Route(&quot;set-speed\/{speed=20:int}&quot;)]\npublic IActionResult SetCarSpeed(int speed)\n<\/code><\/pre>\n<p><b>NOTE<\/b> As discussed in chapter 6, don\u2019t use route constraints for validation. For example, if you call the preceding &quot;set-speed\/{speed=20:int}&quot; route with an invalid value for speed, \/set-speed\/oops, you will get a 404 Not Found response, as the route does not match. Without the int constraint, you would receive the more sensible 400 Bad Request response.<br \/>\n\u6ce8\u610f:\u5982\u7b2c 6 \u7ae0\u6240\u8ff0\uff0c\u4e0d\u8981\u4f7f\u7528 route constraints \u8fdb\u884c\u9a8c\u8bc1\u3002\u4f8b\u5982\uff0c\u5982\u679c\u60a8\u4f7f\u7528\u65e0\u6548\u7684 speed \u503c \/set-speed\/oops \u8c03\u7528\u524d\u9762\u7684 \u201cset-speed\/{speed=20\uff1aint}\u201d \u8def\u7531\uff0c\u60a8\u5c06\u6536\u5230 404 Not Found \u54cd\u5e94\uff0c\u56e0\u4e3a\u8def\u7531\u4e0d\u5339\u914d\u3002\u5982\u679c\u6ca1\u6709 int \u7ea6\u675f\uff0c\u60a8\u5c06\u6536\u5230\u66f4\u660e\u667a\u7684 400 Bad Request \u54cd\u5e94\u3002<\/p>\n<p>If you managed to get your head around routing in chapter 6, routing with web API controllers shouldn\u2019t hold any surprises for you. One thing you might begin noticing when you start using attribute routing with web API controllers is the amount you repeat yourself. Minimal APIs use route groups to reduce duplication, and Razor Pages removes a lot of the repetition by using conventions to calculate route templates based on the Razor Page\u2019s filename. So what can we use with web API controllers?<br \/>\n\u5982\u679c\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u8bbe\u6cd5\u4e86\u89e3\u4e86\u8def\u7531\uff0c\u90a3\u4e48\u4f7f\u7528 Web API \u63a7\u5236\u5668\u8fdb\u884c\u8def\u7531\u5e94\u8be5\u4e0d\u4f1a\u7ed9\u60a8\u5e26\u6765\u4efb\u4f55\u60ca\u559c\u3002\u5f53\u60a8\u5f00\u59cb\u5c06\u5c5e\u6027\u8def\u7531\u4e0e Web API \u63a7\u5236\u5668\u4e00\u8d77\u4f7f\u7528\u65f6\uff0c\u60a8\u53ef\u80fd\u4f1a\u5f00\u59cb\u6ce8\u610f\u5230\u7684\u4e00\u4ef6\u4e8b\u662f\u60a8\u81ea\u5df1\u91cd\u590d\u7684\u91cf\u3002\u6700\u5c0f API \u4f7f\u7528\u8def\u7531\u7ec4\u6765\u51cf\u5c11\u91cd\u590d\uff0c\u800c Razor Pages \u901a\u8fc7\u4f7f\u7528\u7ea6\u5b9a\u6839\u636e Razor Page \u7684\u6587\u4ef6\u540d\u8ba1\u7b97\u8def\u7531\u6a21\u677f\u6765\u6d88\u9664\u5927\u91cf\u91cd\u590d\u3002\u90a3\u4e48\u6211\u4eec\u53ef\u4ee5\u7528\u4ec0\u4e48\u6765\u914d\u5408 Web API \u63a7\u5236\u5668\u5462\uff1f<\/p>\n<h3>20.3.1 Combining route attributes to keep your route templates DRY<\/h3>\n<p>20.3.1 \u7ec4\u5408\u8def\u7531\u5c5e\u6027\u4ee5\u4fdd\u6301\u8def\u7531\u6a21\u677f DRY<\/p>\n<p>Adding route attributes to all of your web API controllers can get a bit tedious, especially if you\u2019re mostly following conventions where your routes have a standard prefix, such as &quot;api&quot; or the controller name. Generally, you\u2019ll want to ensure that you don\u2019t repeat yourself (DRY) when it comes to these strings. The following listing shows two action methods with several [Route] attributes. (This is for demonstration purposes only. Stick to one per action if you can!)<br \/>\n\u5411\u6240\u6709 Web API \u63a7\u5236\u5668\u6dfb\u52a0\u8def\u7531\u5c5e\u6027\u53ef\u80fd\u4f1a\u6709\u70b9\u4e4f\u5473\uff0c\u5c24\u5176\u662f\u5f53\u60a8\u4e3b\u8981\u9075\u5faa\u8def\u7531\u5177\u6709\u6807\u51c6\u524d\u7f00\uff08\u4f8b\u5982\u201capi\u201d\u6216\u63a7\u5236\u5668\u540d\u79f0\uff09\u7684\u7ea6\u5b9a\u65f6\u3002\u901a\u5e38\uff0c\u60a8\u9700\u8981\u786e\u4fdd\u5728\u6d89\u53ca\u8fd9\u4e9b\u5b57\u7b26\u4e32\u65f6\u4e0d\u4f1a\u91cd\u590d\u81ea\u5df1 \uff08DRY\uff09\u3002\u4e0b\u9762\u7684\u5217\u8868\u663e\u793a\u4e86\u5177\u6709\u591a\u4e2a [Route] \u5c5e\u6027\u7684\u4e24\u79cd\u4f5c\u65b9\u6cd5\u3002\uff08\u8fd9\u4ec5\u7528\u4e8e\u6f14\u793a\u76ee\u7684\u3002\u5982\u679c\u53ef\u4ee5\u7684\u8bdd\uff0c\u6bcf\u4e2a\u52a8\u4f5c\u575a\u6301\u4e00\u4e2a\uff01<\/p>\n<p>Listing 20.6 Duplication in RouteAttribute templates<br \/>\n\u5217\u8868 20.6 RouteAttribute \u6a21\u677f\u4e2d\u7684\u91cd\u590d<\/p>\n<pre><code>public class CarController\n{\n    [Route(&quot;api\/car\/start&quot;)]             #A\n    [Route(&quot;api\/car\/ignition&quot;)]          #A\n    [Route(&quot;start-car&quot;)]\n    public IActionResult Start()\n    {\n         \/* method implementation*\/\n    }\n\n    [Route(&quot;api\/car\/speed\/{speed}&quot;)]     #A\n    [Route(&quot;set-speed\/{speed}&quot;)]\n    public IActionResult SetCarSpeed(int speed)\n    {\n         \/* method implementation*\/\n    }\n}<\/code><\/pre>\n<p>\u2776 Multiple route templates use the same \u201capi\/car\u201d prefix.<br \/>\n\u591a\u4e2a\u8def\u7531\u6a21\u677f\u4f7f\u7528\u76f8\u540c\u7684 \u201capi\/car\u201d \u524d\u7f00\u3002<\/p>\n<p>There\u2019s quite a lot of duplication here; you\u2019re adding &quot;api\/car&quot; to most of your routes. Presumably, if you decided to change this to &quot;api\/vehicles&quot;, you\u2019d have to go through each attribute and update it. Code like that is asking for a typo to creep in!<br \/>\n\u8fd9\u91cc\u6709\u5f88\u591a\u91cd\u590d;\u60a8\u6b63\u5728\u5c06 \u201capi\/car\u201d \u6dfb\u52a0\u5230\u5927\u591a\u6570\u8def\u7ebf\u4e2d\u3002\u636e\u63a8\u6d4b\uff0c\u5982\u679c\u60a8\u51b3\u5b9a\u5c06\u5176\u66f4\u6539\u4e3a \u201capi\/vehicles\u201d\uff0c\u5219\u5fc5\u987b\u68c0\u67e5\u6bcf\u4e2a\u5c5e\u6027\u5e76\u66f4\u65b0\u5b83\u3002\u50cf\u8fd9\u6837\u7684\u4ee3\u7801\u5c31\u662f\u8981\u6c42\u4e00\u4e2a\u62fc\u5199\u9519\u8bef\u6084\u6084\u6e9c\u8fdb\u6765\uff01<\/p>\n<p>To alleviate this pain, it\u2019s possible to apply RouteAttributes to controllers, in addition to action methods. When a controller and an action method both have a route attribute, the overall route template for the method is calculated by combining the two templates.<br \/>\n\u4e3a\u4e86\u51cf\u8f7b\u8fd9\u79cd\u75db\u82e6\uff0c\u9664\u4e86\u4f5c\u65b9\u6cd5\u4e4b\u5916\uff0c\u8fd8\u53ef\u4ee5\u5c06 RouteAttributes \u5e94\u7528\u4e8e\u63a7\u5236\u5668\u3002\u5f53\u63a7\u5236\u5668\u548c\u4f5c\u65b9\u6cd5\u90fd\u5177\u6709 route \u5c5e\u6027\u65f6\uff0c\u8be5\u65b9\u6cd5\u7684\u603b\u4f53\u8def\u7531\u6a21\u677f\u662f\u901a\u8fc7\u7ec4\u5408\u4e24\u4e2a\u6a21\u677f\u6765\u8ba1\u7b97\u7684\u3002<\/p>\n<p>Listing 20.7 Combining RouteAttribute templates<br \/>\n\u793a\u4f8b 20.7 \u7ec4\u5408 RouteAttribute \u6a21\u677f<\/p>\n<pre><code>[Route(&quot;api\/car&quot;)]\npublic class CarController\n{\n    [Route(&quot;start&quot;)]          #A\n    [Route(&quot;ignition&quot;)]       #B\n    [Route(&quot;\/start-car&quot;)]          #C\n    public IActionResult Start()\n    {\n         \/* method implementation*\/\n    }\n\n    [Route(&quot;speed\/{speed}&quot;)]          #D\n    [Route(&quot;\/set-speed\/{speed}&quot;)]                  #E\n    public IActionResult SetCarSpeed(int speed)\n    {\n         \/* method implementation*\/\n    }\n}<\/code><\/pre>\n<p>\u2776 Combines to give \u201capi\/car\/start\u201d<br \/>\n\u2777 Combines to give \u201capi\/car\/ignition\u201d<br \/>\n\u2778 Does not combine because it starts with \/; gives the \u201cstart-car\u201d template<br \/>\n\u2779 Combines to give \u201capi\/car\/speed\/{speed}\u201d<br \/>\n\u277a Does not combine because it starts with \/; gives the \u201cset-speed\/{speed}\u201d template<\/p>\n<p>Combining attributes in this way can reduce some of the duplication in your route templates and makes it easier to add or change the prefixes (such as switching &quot;car&quot; to &quot;vehicle&quot;) for multiple action methods. To ignore the RouteAttribute on the controller and create an absolute route template, start your action method route template with a slash (\/). Using a controller RouteAttribute reduces a lot of the duplication, but you can go one better by using token replacement.<br \/>\n\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u7ec4\u5408\u5c5e\u6027\u53ef\u4ee5\u51cf\u5c11\u8def\u7ebf\u6a21\u677f\u4e2d\u7684\u4e00\u4e9b\u91cd\u590d\uff0c\u5e76\u53ef\u4ee5\u66f4\u8f7b\u677e\u5730\u4e3a\u591a\u4e2a\u4f5c\u65b9\u6cd5\u6dfb\u52a0\u6216\u66f4\u6539\u524d\u7f00\uff08\u4f8b\u5982\u5c06 \u201ccar\u201d \u5207\u6362\u4e3a \u201cvehicle\u201d\uff09\u3002\u8981\u5ffd\u7565\u63a7\u5236\u5668\u4e0a\u7684 RouteAttribute \u5e76\u521b\u5efa\u7edd\u5bf9\u8def\u7531\u6a21\u677f\uff0c\u8bf7\u4f7f\u7528\u659c\u6760 \uff08\/\uff09 \u542f\u52a8\u4f5c\u65b9\u6cd5\u8def\u7531\u6a21\u677f\u3002\u4f7f\u7528\u63a7\u5236\u5668 RouteAttribute \u53ef\u4ee5\u51cf\u5c11\u5f88\u591a\u91cd\u590d\uff0c\u4f46\u60a8\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528\u4ee4\u724c\u66ff\u6362\u6765\u66f4\u597d\u5730\u8fdb\u884c\u4e00\u6b21\u91cd\u590d\u3002<\/p>\n<h3>20.3.2 Using token replacement to reduce duplication in attribute routing<\/h3>\n<p>20.3.2 \u4f7f\u7528\u4ee4\u724c\u66ff\u6362\u6765\u51cf\u5c11\u5c5e\u6027\u8def\u7531\u4e2d\u7684\u91cd\u590d<\/p>\n<p>The ability to combine attribute routes is handy, but you\u2019re still left with some duplication if you\u2019re prefixing your routes with the name of the controller, or if your route templates always use the action name. If you wish, you can simplify even further!<br \/>\n\u7ec4\u5408\u5c5e\u6027\u8def\u7531\u7684\u529f\u80fd\u5f88\u65b9\u4fbf\uff0c\u4f46\u5982\u679c\u60a8\u5728\u8def\u7531\u524d\u52a0\u4e0a\u63a7\u5236\u5668\u7684\u540d\u79f0\uff0c\u6216\u8005\u5982\u679c\u60a8\u7684\u8def\u7531\u6a21\u677f\u59cb\u7ec8\u4f7f\u7528\u4f5c\u540d\u79f0\uff0c\u5219\u4ecd\u7136\u4f1a\u7559\u4e0b\u4e00\u4e9b\u91cd\u590d\u3002\u5982\u679c\u60a8\u613f\u610f\uff0c\u60a8\u53ef\u4ee5\u8fdb\u4e00\u6b65\u7b80\u5316\uff01<\/p>\n<p>Attribute routes support the automatic replacement of [action] and [controller] tokens in your attribute routes. These will be replaced with the name of the action and the controller (without the \u201cController\u201d suffix), respectively. The tokens are replaced after all attributes have been combined, which can be useful when you have controller inheritance hierarchies. This listing shows how you can create a BaseController class that applies a consistent route template prefix to all the web API controllers in your application.<br \/>\n\u5c5e\u6027\u8def\u7531\u652f\u6301\u81ea\u52a8\u66ff\u6362\u5c5e\u6027\u8def\u7531\u4e2d\u7684 [action] \u548c [controller] \u4ee4\u724c\u3002\u8fd9\u4e9b\u5c06\u5206\u522b\u66ff\u6362\u4e3a\u4f5c\u548c\u63a7\u5236\u5668\u7684\u540d\u79f0\uff08\u4e0d\u5e26 \u201cController\u201d \u540e\u7f00\uff09\u3002\u5728\u5408\u5e76\u6240\u6709\u5c5e\u6027\u540e\uff0c\u5c06\u66ff\u6362\u4ee4\u724c\uff0c\u8fd9\u5728\u5177\u6709\u63a7\u5236\u5668\u7ee7\u627f\u5c42\u6b21\u7ed3\u6784\u65f6\u975e\u5e38\u6709\u7528\u3002\u6b64\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u521b\u5efa\u4e00\u4e2a BaseController \u7c7b\uff0c\u8be5\u7c7b\u5c06\u4e00\u81f4\u7684\u8def\u7531\u6a21\u677f\u524d\u7f00\u5e94\u7528\u4e8e\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709 Web API \u63a7\u5236\u5668\u3002<\/p>\n<p>Listing 20.8 Token replacement in RouteAttributes<br \/>\n\u6e05\u5355 20.8 RouteAttributes \u4e2d\u7684 Token \u66ff\u6362<\/p>\n<pre><code>[Route(&quot;api\/[controller]&quot;)]                      #A\npublic abstract class BaseController { }       #B\n\npublic class CarController : BaseController\n{\n    [Route(&quot;[action]&quot;)]          #C\n    [Route(&quot;ignition&quot;)]             #D\n    [Route(&quot;\/start-car&quot;)]          #E\n    public IActionResult Start()\n    {\n         \/* method implementation*\/\n    }\n}<\/code><\/pre>\n<p>\u2776 You can apply attributes to a base class, and derived classes will inherit them.<br \/>\n\u2777 Token replacement happens last, so [controller] is replaced with \u201ccar\u201d not \u201cbase\u201d.<br \/>\n\u2778 Combines and replaces tokens to give the \u201capi\/car\/start\u201d template<br \/>\n\u2779 Combines and replaces tokens to give the \u201capi\/car\/ignition\u201d template<br \/>\n\u277a Does not combine with base attributes because it starts with \/, so it remains as \u201cstart-car\u201d<\/p>\n<p><b>Warning<\/b> If you use token replacement for [controller] or [action], remember that renaming classes and methods will change your public API. If that worries you, you can stick to using static strings like &quot;car&quot; instead.<br \/>\n\u8b66\u544a:\u5982\u679c\u4f60\u5bf9 [controller] \u6216 [action] \u4f7f\u7528\u4ee4\u724c\u66ff\u6362\uff0c\u8bf7\u8bb0\u4f4f\u91cd\u547d\u540d\u7c7b\u548c\u65b9\u6cd5\u5c06\u66f4\u6539\u4f60\u7684\u516c\u5171 API\u3002\u5982\u679c\u8fd9\u8ba9\u60a8\u611f\u5230\u62c5\u5fe7\uff0c\u60a8\u53ef\u4ee5\u575a\u6301\u4f7f\u7528\u50cf \u201ccar\u201d \u8fd9\u6837\u7684\u9759\u6001\u5b57\u7b26\u4e32\u3002<\/p>\n<p>When combined with everything you learned in chapter 6, we\u2019ve covered pretty much everything there is to know about attribute routing. There\u2019s just one more thing to consider: handling different HTTP request types like GET and POST.<br \/>\n\u7ed3\u5408\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u5b66\u5230\u7684\u6240\u6709\u5185\u5bb9\uff0c\u6211\u4eec\u51e0\u4e4e\u6db5\u76d6\u4e86\u6709\u5173\u5c5e\u6027\u8def\u7531\u7684\u6240\u6709\u77e5\u8bc6\u3002\u8fd8\u6709\u4e00\u4ef6\u4e8b\u9700\u8981\u8003\u8651\uff1a\u5904\u7406\u4e0d\u540c\u7684 HTTP \u8bf7\u6c42\u7c7b\u578b\uff0c\u5982 GET \u548c POST\u3002<\/p>\n<h3>20.3.3 Handling HTTP verbs with attribute routing<\/h3>\n<p>20.3.3 \u4f7f\u7528\u5c5e\u6027\u8def\u7531\u5904\u7406 HTTP \u52a8\u8bcd<\/p>\n<p>In Razor Pages, the HTTP verb, such as GET or POST, isn\u2019t part of the routing process. The RoutingMiddleware determines which Razor Page to execute based solely on the route template associated with the Razor Page. It\u2019s only when a Razor Page is about to be executed that the HTTP verb is used to decide which page handler to execute: OnGet for the GET verb, or OnPost for the POST verb, for example.<br \/>\n\u5728 Razor Pages \u4e2d\uff0cHTTP \u8c13\u8bcd\uff08\u5982 GET \u6216 POST\uff09\u4e0d\u662f\u8def\u7531\u8fc7\u7a0b\u7684\u4e00\u90e8\u5206\u3002RoutingMiddleware \u4ec5\u6839\u636e\u4e0e Razor Page \u5173\u8054\u7684\u8def\u7531\u6a21\u677f\u6765\u786e\u5b9a\u8981\u6267\u884c\u7684 Razor Page\u3002\u4ec5\u5f53\u5373\u5c06\u6267\u884c Razor \u9875\u9762\u65f6\uff0c\u624d\u4f1a\u4f7f\u7528 HTTP \u8c13\u8bcd\u6765\u51b3\u5b9a\u8981\u6267\u884c\u54ea\u4e2a\u9875\u9762\u5904\u7406\u7a0b\u5e8f\uff1a\u4f8b\u5982\uff0cOnGet \u7528\u4e8e GET \u8c13\u8bcd\uff0c\u6216 OnPost \u7528\u4e8e POST \u8c13\u8bcd\u3002<\/p>\n<p>Web API controllers work like minimal API endpoints: the HTTP verb takes part in the routing process itself. So a GET request may be routed to one action, and a POST request may be routed to a different action, even if the request used the same URL.<br \/>\nWeb API \u63a7\u5236\u5668\u7684\u5de5\u4f5c\u65b9\u5f0f\u7c7b\u4f3c\u4e8e\u6700\u5c0f API \u7aef\u70b9\uff1aHTTP \u52a8\u8bcd\u53c2\u4e0e\u8def\u7531\u8fc7\u7a0b\u672c\u8eab\u3002\u56e0\u6b64\uff0cGET \u8bf7\u6c42\u53ef\u4ee5\u8def\u7531\u5230\u4e00\u4e2a\u4f5c\uff0c\u800c POST \u8bf7\u6c42\u53ef\u4ee5\u8def\u7531\u5230\u4e0d\u540c\u7684\u4f5c\uff0c\u5373\u4f7f\u8bf7\u6c42\u4f7f\u7528\u76f8\u540c\u7684 URL\u3002<\/p>\n<p>The [Route] attribute we\u2019ve used so far responds to all HTTP verbs. Instead, an action should typically only handle a single verb. Instead of the [Route] attribute, you can use<br \/>\n\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u4eec\u4f7f\u7528\u7684 [Route] \u5c5e\u6027\u54cd\u5e94\u6240\u6709 HTTP \u52a8\u8bcd\u3002\u76f8\u53cd\uff0c\u4e00\u4e2a\u4f5c\u901a\u5e38\u5e94\u8be5\u53ea\u5904\u7406\u4e00\u4e2a\u52a8\u8bcd\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 [Route] \u5c5e\u6027<\/p>\n<p>\u2022  [HttpPost] to handle POST requests<br \/>\n\u2022  [HttpGet] to handle GET requests<br \/>\n\u2022  [HttpPut] to handle PUT requests<\/p>\n<p>There are similar attributes for all the standard HTTP verbs, like DELETE and OPTIONS. You can use these attributes instead of the [Route] attribute to specify that an action method should correspond to a single verb, as shown in the following listing.<br \/>\n\u6240\u6709\u6807\u51c6 HTTP \u52a8\u8bcd\u90fd\u6709\u7c7b\u4f3c\u7684\u5c5e\u6027\uff0c\u4f8b\u5982 DELETE \u548c OPTIONS\u3002\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e9b\u5c5e\u6027\u800c\u4e0d\u662f [Route] \u5c5e\u6027\u6765\u6307\u5b9a\u4f5c\u65b9\u6cd5\u5e94\u5bf9\u5e94\u4e8e\u5355\u4e2a\u8c13\u8bcd\uff0c\u5982\u4e0b\u9762\u7684\u5217\u8868\u6240\u793a\u3002<\/p>\n<p>Listing 20.9 Using HTTP verb attributes with attribute routing<br \/>\n\u6e05\u5355 20.9 \u5728\u5c5e\u6027\u8def\u7531\u4e2d\u4f7f\u7528 HTTP \u52a8\u8bcd\u5c5e\u6027<\/p>\n<pre><code>public class AppointmentController\n{\n    [HttpGet(&quot;\/appointments&quot;)]                #A\n    public IActionResult ListAppointments()   #A\n    {                                         #A\n        \/* method implementation *\/           #A\n    }                                         #A\n\n    [HttpPost(&quot;\/appointments&quot;)]                 #B\n    public IActionResult CreateAppointment()    #B\n    {                                           #B\n        \/* method implementation *\/             #B\n    }                                           #B\n}<\/code><\/pre>\n<p>\u2776 Executed only in response to GET \/appointments<br \/>\n\u2777 Executed only in response to POST \/appointments<\/p>\n<p>If your application receives a request that matches the route template of an action method but doesn\u2019t match the required HTTP verb, you\u2019ll get a 405 Method not allowed error response. For example, if you send a DELETE request to the \/appointments URL in the previous listing, you\u2019ll get a 405 error response.<br \/>\n\u5982\u679c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6536\u5230\u4e0e\u4f5c\u65b9\u6cd5\u7684\u8def\u7531\u6a21\u677f\u5339\u914d\u4f46\u4e0e\u6240\u9700\u7684 HTTP \u52a8\u8bcd\u4e0d\u5339\u914d\u7684\u8bf7\u6c42\uff0c\u60a8\u5c06\u6536\u5230 405 Method not allowed \u9519\u8bef\u54cd\u5e94\u3002\u4f8b\u5982\uff0c\u5982\u679c\u60a8\u5411\u4e0a\u4e00\u4e2a\u5217\u8868\u4e2d\u7684 \/appointments URL \u53d1\u9001 DELETE \u8bf7\u6c42\uff0c\u60a8\u5c06\u6536\u5230 405 \u9519\u8bef\u54cd\u5e94\u3002<\/p>\n<p>When you\u2019re building web API controllers, there is some code that you\u2019ll find yourself writing repeatedly. The [ApiController] attribute is designed to handle some of this for you and reduce the amount of boilerplate you need.<br \/>\n\u5728\u6784\u5efa Web API \u63a7\u5236\u5668\u65f6\uff0c\u60a8\u4f1a\u53d1\u73b0\u81ea\u5df1\u9700\u8981\u91cd\u590d\u7f16\u5199\u4e00\u4e9b\u4ee3\u7801\u3002[ApiController] \u5c5e\u6027\u65e8\u5728\u4e3a\u60a8\u5904\u7406\u5176\u4e2d\u7684\u4e00\u4e9b\u95ee\u9898\uff0c\u5e76\u51cf\u5c11\u60a8\u9700\u8981\u7684\u6837\u677f\u6570\u91cf\u3002<\/p>\n<h2>20.4 Using common conventions with [ApiController]<\/h2>\n<p>20.4 \u5728 [ApiController] \u4e2d\u4f7f\u7528\u901a\u7528\u7ea6\u5b9a<\/p>\n<p>In this section you\u2019ll learn about the [ApiController] attribute and how it can reduce the amount of code you need to write to create consistent web API controllers. You\u2019ll learn about the conventions it applies, why they\u2019re useful, and how to turn them off if you need to.<br \/>\n\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3 [ApiController] \u5c5e\u6027\uff0c\u4ee5\u53ca\u5b83\u5982\u4f55\u51cf\u5c11\u521b\u5efa\u4e00\u81f4\u7684 Web API \u63a7\u5236\u5668\u6240\u9700\u7684\u4ee3\u7801\u91cf\u3002\u60a8\u5c06\u4e86\u89e3\u5b83\u9002\u7528\u7684\u7ea6\u5b9a\u3001\u5b83\u4eec\u4e3a\u4ec0\u4e48\u6709\u7528\uff0c\u4ee5\u53ca\u5982\u4f55\u5728\u9700\u8981\u65f6\u5173\u95ed\u5b83\u4eec\u3002<\/p>\n<p>The [ApiController] attribute was introduced in .NET Core 2.1 to simplify the process of creating web API controllers. To understand what it does, it\u2019s useful to look at an example of how you might write a web API controller without the [ApiController] attribute and compare that with the code required to achieve the same thing with the attribute.<br \/>\n[ApiController] \u5c5e\u6027\u662f\u5728 .NET Core 2.1 \u4e2d\u5f15\u5165\u7684\uff0c\u7528\u4e8e\u7b80\u5316\u521b\u5efa Web API \u63a7\u5236\u5668\u7684\u8fc7\u7a0b\u3002\u8981\u4e86\u89e3\u5b83\u7684\u4f5c\u7528\uff0c\u67e5\u770b\u4e00\u4e2a\u793a\u4f8b\uff0c\u4e86\u89e3\u5982\u4f55\u7f16\u5199\u6ca1\u6709 [ApiController] \u5c5e\u6027\u7684 Web API \u63a7\u5236\u5668\uff0c\u5e76\u5c06\u5176\u4e0e\u4f7f\u7528\u8be5\u5c5e\u6027\u5b9e\u73b0\u76f8\u540c\u4f5c\u6240\u9700\u7684\u4ee3\u7801\u8fdb\u884c\u6bd4\u8f83\uff0c\u8fd9\u975e\u5e38\u6709\u7528\u3002<\/p>\n<p>Listing 20.10 Creating a web API controller without the [ApiController] attribute<br \/>\n\u6e05\u5355 20.10 \u521b\u5efa\u4e0d\u5e26 [ApiController] \u5c5e\u6027\u7684 Web API \u63a7\u5236\u5668<\/p>\n<pre><code>public class FruitController : ControllerBase\n{\n    List&lt;string&gt; _fruit = new List&lt;string&gt;     #A\n    {                                          #A\n        &quot;Pear&quot;, &quot;Lemon&quot;, &quot;Peach&quot;               #A\n    };                                         #A\n\n    [HttpPost(&quot;fruit&quot;)]                              #B\n    public ActionResult Update([FromBody] UpdateModel model)     #C\n    {\n        if (!ModelState.IsValid)                             #D\n        {                                                    #D\n             return BadRequest(                              #D\n                new ValidationProblemDetails(ModelState));   #D\n        }                                                    #D\n\n        if (model.Id &lt; 0 || model.Id &gt; _fruit.Count)             \n        {\n            return NotFound(new ProblemDetails()              #E\n            {                                                 #E\n                Status = 404,                                 #E\n                Title = &quot;Not Found&quot;,                          #E\n                Type = &quot;https:\/\/tools.ietf.org\/html\/rfc7231&quot;  #E\n                       + &quot;#section-6.5.4&quot;,                    #E\n            });                                               #E\n        }                                                     #E\n        _fruit[model.Id] = model.Name;    #F\n        return Ok();                      #F\n    }\n\n    public class UpdateModel                                    \n    {                                                           \n        public int Id { get; set; }                             \n\n        [Required]                         #G\n        public string Name { get; set; }   #G\n    }\n}<\/code><\/pre>\n<p>\u2776 The list of strings serves as the application model in this example.<br \/>\n\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u5b57\u7b26\u4e32\u5217\u8868\u7528\u4f5c\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u3002<br \/>\n\u2777 Web APIs use attribute routing to define the route templates.<br \/>\nWeb API \u4f7f\u7528\u5c5e\u6027\u8def\u7531\u6765\u5b9a\u4e49\u8def\u7531\u6a21\u677f\u3002<br \/>\n\u2778 The [FromBody] attribute indicates that the parameter should be bound to the request body.<br \/>\n[FromBody] \u5c5e\u6027\u6307\u793a\u53c2\u6570\u5e94\u7ed1\u5b9a\u5230\u8bf7\u6c42\u6b63\u6587\u3002<br \/>\n\u2779 You need to check if model validation succeeded and return a 400 response if it failed.<br \/>\n\u60a8\u9700\u8981\u68c0\u67e5\u6a21\u578b\u9a8c\u8bc1\u662f\u5426\u6210\u529f\uff0c\u5982\u679c\u5931\u8d25\uff0c\u5219\u8fd4\u56de 400 \u54cd\u5e94\u3002<br \/>\n\u277a If the data sent does not contain a valid ID, returns a 404 ProblemDetails response<br \/>\n\u5982\u679c\u53d1\u9001\u7684\u6570\u636e\u4e0d\u5305\u542b\u6709\u6548\u7684 ID\uff0c\u5219\u8fd4\u56de 404 ProblemDetails \u54cd\u5e94<br \/>\n\u277b Updates the model and returns a 200 Response<br \/>\n\u66f4\u65b0\u6a21\u578b\u5e76\u8fd4\u56de 200 \u54cd\u5e94<br \/>\n\u277c UpdateModel is valid only if the Name value is provided, as set by the [Required] attribute.<br \/>\nUpdateModel \u4ec5\u5728\u63d0\u4f9b Name \u503c\u65f6\u6709\u6548\uff0c\u5982 [Required] \u5c5e\u6027\u6240\u8bbe\u7f6e\u3002<\/p>\n<p>This example demonstrates many common features and patterns used with web API controllers:<br \/>\n\u6b64\u793a\u4f8b\u6f14\u793a\u4e86\u4e0e Web API \u63a7\u5236\u5668\u4e00\u8d77\u4f7f\u7528\u7684\u8bb8\u591a\u5e38\u89c1\u529f\u80fd\u548c\u6a21\u5f0f\uff1a<\/p>\n<p>\u2022  Web API controllers read data from the body of a request, typically sent as JSON. To ensure the body is read as JSON and not as form values, you have to apply the [FromBody] attribute to the method parameters to ensure it is model-bound correctly.<br \/>\nWeb API \u63a7\u5236\u5668\u4ece\u8bf7\u6c42\u6b63\u6587\u4e2d\u8bfb\u53d6\u6570\u636e\uff0c\u901a\u5e38\u4ee5 JSON \u5f62\u5f0f\u53d1\u9001\u3002\u82e5\u8981\u786e\u4fdd\u5c06\u6b63\u6587\u8bfb\u53d6\u4e3a JSON \u800c\u4e0d\u662f\u8868\u5355\u503c\uff0c\u5fc5\u987b\u5c06 [FromBody] \u5c5e\u6027\u5e94\u7528\u4e8e\u65b9\u6cd5\u53c2\u6570\uff0c\u4ee5\u786e\u4fdd\u5176\u6a21\u578b\u7ed1\u5b9a\u6b63\u786e\u3002<\/p>\n<p>\u2022  As discussed in chapter 16, after model binding, the model is validated, but it\u2019s up to you to act on the validation results. You should return a 400 Bad Request response if the values provided failed validation. You typically want to provide details of why the request was invalid: this is done in listing 20.10 by returning a ValidationProblemDetails object in the response body, built from the ModelState.<br \/>\n\u5982\u7b2c 16 \u7ae0\u6240\u8ff0\uff0c\u5728\u6a21\u578b\u7ed1\u5b9a\u4e4b\u540e\uff0c\u5c06\u9a8c\u8bc1\u6a21\u578b\uff0c\u4f46\u7531\u60a8\u6839\u636e\u9a8c\u8bc1\u7ed3\u679c\u6267\u884c\u4f5c\u3002\u5982\u679c\u63d0\u4f9b\u7684\u503c\u9a8c\u8bc1\u5931\u8d25\uff0c\u5219\u5e94\u8fd4\u56de 400 Bad Request \u54cd\u5e94\u3002\u60a8\u901a\u5e38\u5e0c\u671b\u63d0\u4f9b\u6709\u5173\u8bf7\u6c42\u65e0\u6548\u539f\u56e0\u7684\u8be6\u7ec6\u4fe1\u606f\uff1a\u8fd9\u662f\u5728\u6e05\u5355 20.10 \u4e2d\u901a\u8fc7\u5728\u54cd\u5e94\u6b63\u6587\u4e2d\u8fd4\u56de\u4e00\u4e2a ValidationProblemDetails \u5bf9\u8c61\u6765\u5b8c\u6210\u7684\uff0c\u8be5\u5bf9\u8c61\u662f\u4ece ModelState \u6784\u5efa\u7684\u3002<\/p>\n<p>\u2022  Whenever you return an error status, such as a 404 Not Found, where possible you should return details of the problem that will allow the caller to diagnose the issue. The ProblemDetails class is the recommended way of doing that in ASP.NET Core.<br \/>\n\u6bcf\u5f53\u8fd4\u56de\u9519\u8bef\u72b6\u6001\uff08\u5982 404 Not Found\uff09\u65f6\uff0c\u5e94\u5c3d\u53ef\u80fd\u8fd4\u56de\u95ee\u9898\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u4ee5\u4fbf\u8c03\u7528\u65b9\u8bca\u65ad\u95ee\u9898\u3002ProblemDetails \u7c7b\u662f\u5728 ASP.NET Core \u4e2d\u6267\u884c\u6b64\u4f5c\u7684\u63a8\u8350\u65b9\u6cd5\u3002<\/p>\n<p>The code in listing 20.10 is representative of what you might see in an ASP.NET Core API controller before .NET Core 2.1. The introduction of the [ApiController] attribute in .NET Core 2.1 (and subsequent refinement in later versions) makes this same code much simpler, as shown in the following listing.<br \/>\n\u6e05\u5355 20.10 \u4e2d\u7684\u4ee3\u7801\u4ee3\u8868\u4e86\u60a8\u5728 .NET Core 2.1 \u4e4b\u524d\u7684 ASP.NET Core API \u63a7\u5236\u5668\u4e2d\u53ef\u80fd\u770b\u5230\u7684\u5185\u5bb9\u3002\u5728 .NET Core 2.1 \u4e2d\u5f15\u5165 [ApiController] \u5c5e\u6027\uff08\u4ee5\u53ca\u66f4\u9ad8\u7248\u672c\u4e2d\u7684\u540e\u7eed\u4f18\u5316\uff09\u4f7f\u76f8\u540c\u7684\u4ee3\u7801\u53d8\u5f97\u66f4\u52a0\u7b80\u5355\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 20.11 Creating a web API controller with the [ApiController] attribute<br \/>\n\u6e05\u5355 20.11 \u521b\u5efa\u5177\u6709 [ApiController] \u5c5e\u6027\u7684 Web API \u63a7\u5236\u5668<\/p>\n<pre><code>[ApiController]                               #A\npublic class FruitController : ControllerBase\n{\n    List&lt;string&gt; _fruit = new List&lt;string&gt;\n    {\n        &quot;Pear&quot;, &quot;Lemon&quot;, &quot;Peach&quot;\n    };\n\n    [HttpPost(&quot;fruit&quot;)]\n    public ActionResult Update(UpdateModel model)    #B\n    {                                                    #C\n        if (model.Id &lt; 0 || model.Id &gt; _fruit.Count)\n        {\n            return NotFound();   #D\n        }\n\n        _fruit[model.Id] = model.Name;\n\n        return Ok();\n    }\n\n    public class UpdateModel\n    {\n        public int Id { get; set; }\n\n        [Required]\n        public string Name { get; set; }\n    }\n}<\/code><\/pre>\n<p>\u2776 Adding the [ApiController] attribute applies several conventions common to API controllers.<br \/>\n\u6dfb\u52a0 [ApiController] \u5c5e\u6027\u5c06\u5e94\u7528 API \u63a7\u5236\u5668\u901a\u7528\u7684\u51e0\u4e2a\u7ea6\u5b9a\u3002<br \/>\n\u2777 The [FromBody] attribute is assumed for complex action method parameters.<br \/>\n[FromBody] \u5c5e\u6027\u5047\u5b9a\u7528\u4e8e\u590d\u6742\u7684\u4f5c\u65b9\u6cd5\u53c2\u6570\u3002<br \/>\n\u2778 The model validation is automatically checked, and if invalid, returns a 400 response.<br \/>\n\u7cfb\u7edf\u4f1a\u81ea\u52a8\u68c0\u67e5\u6a21\u578b\u9a8c\u8bc1\uff0c\u5982\u679c\u65e0\u6548\uff0c\u5219\u8fd4\u56de 400 \u54cd\u5e94\u3002<br \/>\n\u2779 Error status codes are automatically converted to a ProblemDetails object.<br \/>\n\u9519\u8bef\u72b6\u6001\u4ee3\u7801\u4f1a\u81ea\u52a8\u8f6c\u6362\u4e3a ProblemDetails \u5bf9\u8c61\u3002<\/p>\n<p>If you compare listing 20.10 with listing 20.11, you\u2019ll see that all the bold code in listing 20.10 can be removed and replaced with the [ApiController] attribute in listing 20.11. The [ApiController] attribute automatically applies several conventions to your controllers:<br \/>\n\u5982\u679c\u4f60\u6bd4\u8f83\u6e05\u5355 20.10 \u548c\u6e05\u5355 20.11\uff0c\u4f60\u4f1a\u53d1\u73b0\u6e05\u5355 20.10 \u4e2d\u7684\u6240\u6709\u7c97\u4f53\u4ee3\u7801\u90fd\u53ef\u4ee5\u5220\u9664\uff0c\u5e76\u66ff\u6362\u4e3a\u6e05\u5355 20.11 \u4e2d\u7684 [ApiController] \u5c5e\u6027\u3002[ApiController] \u5c5e\u6027\u4f1a\u81ea\u52a8\u5c06\u591a\u4e2a\u7ea6\u5b9a\u5e94\u7528\u4e8e\u63a7\u5236\u5668\uff1a<\/p>\n<p>\u2022  Attribute routing\u2014You must use attribute routing with your controllers; you can\u2019t use conventional routing\u2014not that you would, as we\u2019ve discussed this approach only for API controllers anyway.<br \/>\n\u5c5e\u6027\u8def\u7531 - \u5fc5\u987b\u5bf9\u63a7\u5236\u5668\u4f7f\u7528\u5c5e\u6027\u8def\u7531;\u60a8\u4e0d\u80fd\u4f7f\u7528\u4f20\u7edf\u8def\u7531 \u2014 \u5e76\u4e0d\u662f\u8bf4\u60a8\u4f1a\u8fd9\u6837\u505a\uff0c\u56e0\u4e3a\u65e0\u8bba\u5982\u4f55\uff0c\u6211\u4eec\u53ea\u9488\u5bf9 API \u63a7\u5236\u5668\u8ba8\u8bba\u4e86\u8fd9\u79cd\u65b9\u6cd5\u3002<br \/>\n\u2022  Automatic 400 responses\u2014I said in chapter 16 that you should always check the value of ModelState.IsValid in your Razor Page handlers and MVC actions, but the [ApiController] attribute does this for you by adding a filter, as we did with minimal APIs in chapter 7. We\u2019ll cover MVC filters in detail in chapters 21 and 22.<br \/>\n\u81ea\u52a8 400 \u54cd\u5e94 - \u6211\u5728\u7b2c 16 \u7ae0\u4e2d\u8bf4\u8fc7\uff0c\u60a8\u5e94\u8be5\u59cb\u7ec8\u5728 Razor \u9875\u9762\u5904\u7406\u7a0b\u5e8f\u548c MVC\u4f5c\u4e2d\u68c0\u67e5 ModelState.IsValid \u7684\u503c\uff0c\u4f46 [ApiController] \u5c5e\u6027\u901a\u8fc7\u6dfb\u52a0\u8fc7\u6ee4\u5668\u6765\u4e3a\u60a8\u6267\u884c\u6b64\u4f5c\uff0c\u5c31\u50cf\u6211\u4eec\u5728\u7b2c 7 \u7ae0\u4e2d\u5bf9\u6700\u5c11\u7684 API \u6240\u505a\u7684\u90a3\u6837\u3002\u6211\u4eec\u5c06\u5728\u7b2c 21 \u7ae0\u548c\u7b2c 22 \u7ae0\u4e2d\u8be6\u7ec6\u4ecb\u7ecd MVC \u8fc7\u6ee4\u5668\u3002<br \/>\n\u2022  Model binding source inference\u2014Without the [ApiController] attribute, complex types are assumed to be passed as form values in the request body. For web APIs, it\u2019s much more common to pass data as JSON, which ordinarily requires adding the [FromBody] attribute. The [ApiController] attribute takes care of that for you.<br \/>\n\u6a21\u578b\u7ed1\u5b9a\u6e90\u63a8\u7406 - \u5982\u679c\u6ca1\u6709 [ApiController] \u5c5e\u6027\uff0c\u5219\u5047\u5b9a\u590d\u6742\u7c7b\u578b\u5728\u8bf7\u6c42\u6b63\u6587\u4e2d\u4f5c\u4e3a\u8868\u5355\u503c\u4f20\u9012\u3002\u5bf9\u4e8e Web API\uff0c\u5c06\u6570\u636e\u4f5c\u4e3a JSON \u4f20\u9012\u66f4\u4e3a\u5e38\u89c1\uff0c\u8fd9\u901a\u5e38\u9700\u8981\u6dfb\u52a0 [FromBody] \u5c5e\u6027\u3002[ApiController] \u5c5e\u6027\u4f1a\u4e3a\u60a8\u5904\u7406\u8be5\u95ee\u9898\u3002<br \/>\n\u2022  ProblemDetails for error codes\u2014You often want to return a consistent set of data when an error occurs in your API. The [ApiController] attribute intercepts any error status codes returned by your controller (for example, a 404 Not Found response), and converts them to ProblemDetails responses.<br \/>\n\u9519\u8bef\u4ee3\u7801\u7684 ProblemDetails - \u5f53 API \u4e2d\u53d1\u751f\u9519\u8bef\u65f6\uff0c\u60a8\u901a\u5e38\u5e0c\u671b\u8fd4\u56de\u4e00\u7ec4\u4e00\u81f4\u7684\u6570\u636e\u3002[ApiController] \u5c5e\u6027\u622a\u83b7\u63a7\u5236\u5668\u8fd4\u56de\u7684\u4efb\u4f55\u9519\u8bef\u72b6\u6001\u4ee3\u7801\uff08\u4f8b\u5982\uff0c404 Not Found \u54cd\u5e94\uff09\uff0c\u5e76\u5c06\u5176\u8f6c\u6362\u4e3a ProblemDetails \u54cd\u5e94\u3002<\/p>\n<p>When it was introduced, a key feature of the [ApiController] attribute was the Problem Details support, but as I described in chapter 5, the same automatic conversion to Problem Details is now supported by the default ExceptionHandlerMiddleware and StatusCodePagesMiddleware. Nevertheless, the [ApiController] conventions can significantly reduce the amount of boilerplate code you have to write and ensure that validation failures are handled automatically, for example.<br \/>\n\u5f15\u5165 [ApiController] \u5c5e\u6027\u65f6\uff0c\u5b83\u7684\u4e00\u4e2a\u5173\u952e\u529f\u80fd\u662f Problem Details \u652f\u6301\uff0c\u4f46\u6b63\u5982\u6211\u5728\u7b2c 5 \u7ae0\u4e2d\u63cf\u8ff0\u7684\uff0c\u9ed8\u8ba4\u7684 ExceptionHandlerMiddleware \u548c StatusCodePagesMiddleware \u73b0\u5728\u652f\u6301\u76f8\u540c\u7684\u81ea\u52a8\u8f6c\u6362\u4e3a Problem Details\u3002\u5c3d\u7ba1\u5982\u6b64\uff0c[ApiController] \u7ea6\u5b9a\u53ef\u4ee5\u663e\u8457\u51cf\u5c11\u60a8\u5fc5\u987b\u7f16\u5199\u7684\u6837\u677f\u4ee3\u7801\u91cf\uff0c\u5e76\u786e\u4fdd\u81ea\u52a8\u5904\u7406\u9a8c\u8bc1\u5931\u8d25\u3002<\/p>\n<p>As is common in ASP.NET Core, you will be most productive if you follow the conventions rather than trying to fight them. However, if you don\u2019t like some of the conventions introduced by [ApiController],or want to customize them, you can easily do so.<br \/>\n\u6b63\u5982 ASP.NET Core \u4e2d\u7684\u5e38\u89c1\u505a\u6cd5\u4e00\u6837\uff0c\u5982\u679c\u60a8\u9075\u5faa\u60ef\u4f8b\u800c\u4e0d\u662f\u8bd5\u56fe\u4e0e\u4e4b\u6297\u4e89\uff0c\u60a8\u5c06\u6700\u6709\u6548\u7387\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u4e0d\u559c\u6b22 [ApiController] \u5f15\u5165\u7684\u67d0\u4e9b\u7ea6\u5b9a\uff0c\u6216\u8005\u60f3\u8981\u81ea\u5b9a\u4e49\u5b83\u4eec\uff0c\u5219\u53ef\u4ee5\u8f7b\u677e\u5b8c\u6210\u3002<\/p>\n<p>You can customize the web API controller conventions your application uses by calling ConfigureApiBehaviorOptions() on the IMvcBuilder object returned from the AddControllers() method in your Program.cs file. For example, you could disable the automatic 400 responses on validation failure, as shown in the following listing.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5bf9\u4ece Program.cs \u6587\u4ef6\u4e2d\u7684 AddControllers\uff08\uff09 \u65b9\u6cd5\u8fd4\u56de\u7684 IMvcBuilder \u5bf9\u8c61\u8c03\u7528 ConfigureApiBehaviorOptions\uff08\uff09 \u6765\u81ea\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528\u7684 Web API \u63a7\u5236\u5668\u7ea6\u5b9a\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u5728\u9a8c\u8bc1\u5931\u8d25\u65f6\u7981\u7528\u81ea\u52a8 400 \u54cd\u5e94\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 20.12 Customizing [ApiAttribute] behaviors<br \/>\n\u6e05\u5355 20.12 \u81ea\u5b9a\u4e49 [ApiAttribute] \u884c\u4e3a<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers();\n    .ConfigureApiBehaviorOptions(options =&gt;         #A\n    {\n        options.SuppressModelStateInvalidFilter = true;    #B\n    });\n\n\/\/ ...<\/code><\/pre>\n<p>\u2776 Controls which conventions are applied by providing a configuration lambda<br \/>\n\u901a\u8fc7\u63d0\u4f9b\u914d\u7f6e lambda\u6765\u63a7\u5236\u5e94\u7528\u54ea\u4e9b\u7ea6\u5b9a<br \/>\n\u2777 This would disable the automatic 400 responses for invalid requests.<br \/>\n\u5c06\u7981\u7528\u65e0\u6548\u8bf7\u6c42\u7684\u81ea\u52a8 400 \u54cd\u5e94\u3002<\/p>\n<p><b>Tip<\/b> You can disable all the automatic features enabled by the [ApiController] attribute, but I encourage you to stick to the defaults unless you really need to change them. You can read more about disabling features in the documentation at <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/web-api\">https:\/\/docs.microsoft.com\/aspnet\/core\/web-api<\/a>.<br \/>\n\u63d0\u793a:\u60a8\u53ef\u4ee5\u7981\u7528 [ApiController] \u5c5e\u6027\u542f\u7528\u7684\u6240\u6709\u81ea\u52a8\u529f\u80fd\uff0c\u4f46\u6211\u5efa\u8bae\u60a8\u575a\u6301\u4f7f\u7528\u9ed8\u8ba4\u503c\uff0c\u9664\u975e\u60a8\u786e\u5b9e\u9700\u8981\u66f4\u6539\u5b83\u4eec\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/web-api\">https:\/\/docs.microsoft.com\/aspnet\/core\/web-api<\/a> \u4e0a\u7684\u6587\u6863\u4e2d\u9605\u8bfb\u6709\u5173\u7981\u7528\u529f\u80fd\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<p>The ability to customize each aspect of your web API controllers is one of the key differentiators with minimal APIs. In the next section you\u2019ll learn how to control the format of the data returned by your web API controllers\u2014whether that\u2019s JSON, XML, or a different, custom format.<br \/>\n\u81ea\u5b9a\u4e49 Web API \u63a7\u5236\u5668\u5404\u4e2a\u65b9\u9762\u7684\u80fd\u529b\u662f\u6700\u5c11 API \u7684\u5173\u952e\u533a\u522b\u4e4b\u4e00\u3002\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u63a7\u5236 Web API \u63a7\u5236\u5668\u8fd4\u56de\u7684\u6570\u636e\u7684\u683c\u5f0f\uff0c\u65e0\u8bba\u662f JSON\u3001XML \u8fd8\u662f\u5176\u4ed6\u81ea\u5b9a\u4e49\u683c\u5f0f\u3002<\/p>\n<h2>20.5 Generating a response from a model<\/h2>\n<p>20.5 \u4ece\u6a21\u578b\u751f\u6210\u54cd\u5e94<\/p>\n<p>This brings us to the final topic in this chapter: formatting a response. It\u2019s common for API controllers to return JSON these days, but that\u2019s not always the case. In this section you\u2019ll learn about content negotiation and how to enable additional output formats such as XML.<br \/>\n\u8fd9\u5c31\u5f15\u51fa\u4e86\u672c\u7ae0\u7684\u6700\u540e\u4e00\u4e2a\u4e3b\u9898\uff1a\u683c\u5f0f\u5316\u54cd\u5e94\u3002\u5982\u4eca\uff0cAPI \u63a7\u5236\u5668\u8fd4\u56de JSON \u5f88\u5e38\u89c1\uff0c\u4f46\u60c5\u51b5\u5e76\u975e\u603b\u662f\u5982\u6b64\u3002\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5185\u5bb9\u534f\u5546\u4ee5\u53ca\u5982\u4f55\u542f\u7528\u5176\u4ed6\u8f93\u51fa\u683c\u5f0f\uff0c\u4f8b\u5982 XML\u3002<\/p>\n<p>Consider this scenario: you\u2019ve created a web API action method for returning a list of cars, as in the following listing. It invokes a method on your application model, which hands back the list of data to the controller. Now you need to format the response and return it to the caller.<br \/>\n\u8bf7\u8003\u8651\u4ee5\u4e0b\u573a\u666f\uff1a\u60a8\u521b\u5efa\u4e86\u4e00\u4e2a Web API\u4f5c\u65b9\u6cd5\uff0c\u7528\u4e8e\u8fd4\u56de\u6c7d\u8f66\u5217\u8868\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002\u5b83\u8c03\u7528\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u4e0a\u7684\u4e00\u4e2a\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u5c06\u6570\u636e\u5217\u8868\u8fd4\u56de\u7ed9\u63a7\u5236\u5668\u3002\u73b0\u5728\uff0c\u60a8\u9700\u8981\u8bbe\u7f6e\u54cd\u5e94\u7684\u683c\u5f0f\u5e76\u5c06\u5176\u8fd4\u56de\u7ed9\u8c03\u7528\u65b9\u3002<\/p>\n<p>Listing 20.13 A web API controller to return a list of cars<br \/>\n\u5217\u8868 20.13 \u8fd4\u56de\u6c7d\u8f66\u5217\u8868\u7684 Web API \u63a7\u5236\u5668<\/p>\n<pre><code>[ApiController]\npublic class CarsController : Controller\n{\n    [HttpGet(&quot;api\/cars&quot;)]              #A\n    public IEnumerable&lt;string&gt; ListCars()      #B\n    {\n        return new string[]                      #C\n            { &quot;Nissan Micra&quot;, &quot;Ford Focus&quot; };    #C\n    }\n}<\/code><\/pre>\n<p>\u2776 The action is executed with a request to GET \/api\/cars.<br \/>\n\u901a\u8fc7\u8bf7\u6c42 GET \/api\/cars \u6765\u6267\u884c\u4f5c\u3002<br \/>\n\u2777 The API model containing the data is an <code>IEnumerable&lt;string&gt;<\/code>.<br \/>\n\u5305\u542b\u6570\u636e\u7684 API \u6a21\u578b\u662f <code>IEnumerable&lt;string&gt;<\/code>\u3002<br \/>\n\u2778 This data would normally be fetched from the application model.<br \/>\n\u6b64\u6570\u636e\u901a\u5e38\u4ece\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u4e2d\u63d0\u53d6\u3002<\/p>\n<p>You saw in section 20.2 that it\u2019s possible to return data directly from an action method, in which case the middleware formats it and returns the formatted data to the caller. But how does the middleware know which format to use? After all, you could serialize it as JSON, as XML, or even with a simple ToString() call.<br \/>\n\u4f60\u5728 Section 20.2 \u4e2d\u770b\u5230\uff0c\u53ef\u4ee5\u76f4\u63a5\u4ece action \u65b9\u6cd5\u8fd4\u56de\u6570\u636e\uff0c\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u4e2d\u95f4\u4ef6\u4f1a\u5bf9\u5176\u8fdb\u884c\u683c\u5f0f\u5316\u5e76\u5c06\u683c\u5f0f\u5316\u7684\u6570\u636e\u8fd4\u56de\u7ed9\u8c03\u7528\u8005\u3002\u4f46\u662f\u4e2d\u95f4\u4ef6\u5982\u4f55\u77e5\u9053\u8981\u4f7f\u7528\u54ea\u79cd\u683c\u5f0f\u5462\uff1f\u6bd5\u7adf\uff0c\u60a8\u53ef\u4ee5\u5c06\u5176\u5e8f\u5217\u5316\u4e3a JSON\u3001XML\uff0c\u751a\u81f3\u4f7f\u7528\u7b80\u5355\u7684 ToString\uff08\uff09 \u8c03\u7528\u3002<\/p>\n<p><b>Warning<\/b> Remember that in this chapter I\u2019m talking only about web API controller responses. Minimal APIs support only automatic serialization to JSON, nothing else.<br \/>\n\u8b66\u544a:\u8bf7\u8bb0\u4f4f\uff0c\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u53ea\u8ba8\u8bba Web API \u63a7\u5236\u5668\u54cd\u5e94\u3002Minimal API \u4ec5\u652f\u6301\u81ea\u52a8\u5e8f\u5217\u5316\u4e3a JSON\uff0c\u4e0d\u652f\u6301\u5176\u4ed6\u4efb\u4f55\u5185\u5bb9\u3002<\/p>\n<p>The process of determining the format of data to send to clients is known generally as content negotiation (conneg). At a high level, the client sends a header indicating the types of content it can understand\u2014the Accept header\u2014and the server picks one of these, formats the response, and sends a Content-Type header in the response, indicating which type it chose.<br \/>\n\u786e\u5b9a\u8981\u53d1\u9001\u7ed9\u5ba2\u6237\u7aef\u7684\u6570\u636e\u683c\u5f0f\u7684\u8fc7\u7a0b\u901a\u5e38\u79f0\u4e3a\u5185\u5bb9\u534f\u5546 \uff08conneg\uff09\u3002\u6982\u62ec\u5730\u8bf4\uff0c\u5ba2\u6237\u7aef\u53d1\u9001\u4e00\u4e2a\u6807\u5934\uff08\u6307\u793a\u5b83\u53ef\u4ee5\u7406\u89e3\u7684\u5185\u5bb9\u7c7b\u578b\uff09\uff08Accept \u6807\u5934\uff09\uff0c\u670d\u52a1\u5668\u9009\u62e9\u5176\u4e2d\u4e00\u4e2a\u6807\u5934\uff0c\u8bbe\u7f6e\u54cd\u5e94\u7684\u683c\u5f0f\uff0c\u5e76\u5728\u54cd\u5e94\u4e2d\u53d1\u9001 Content-Type \u6807\u5934\uff0c\u6307\u793a\u5b83\u9009\u62e9\u4e86\u54ea\u79cd\u7c7b\u578b\u3002<\/p>\n<blockquote>\n<p>The Accept and Content-Type headers<br \/>\nAccept \u548c Content-Type \u6807\u5934<\/p>\n<p>The Accept header is sent by a client as part of a request to indicate the type of content that the client can handle. It consists of a number of MIME types, with optional weightings (from 0 to 1) to indicate which type would be preferred. For example, the application\/json,text\/xml;q=0.9,text\/plain;q=0.6 header indicates that the client can accept JSON, XML, and plain text, with weightings of 1.0, 0.9, and 0.6, respectively. JSON has a weighting of 1.0, as no explicit weighting was provided. The weightings can be used during content negotiation to choose an optimal representation for both parties.<br \/>\nAccept \u6807\u5934\u7531\u5ba2\u6237\u7aef\u4f5c\u4e3a\u8bf7\u6c42\u7684\u4e00\u90e8\u5206\u53d1\u9001\uff0c\u7528\u4e8e\u6307\u793a\u5ba2\u6237\u7aef\u53ef\u4ee5\u5904\u7406\u7684\u5185\u5bb9\u7c7b\u578b\u3002\u5b83\u7531\u8bb8\u591a MIME \u7c7b\u578b\u7ec4\u6210\uff0c\u5177\u6709\u53ef\u9009\u7684\u6743\u91cd\uff08\u4ece 0 \u5230 1\uff09\u4ee5\u6307\u793a\u9996\u9009\u7c7b\u578b\u3002\u4f8b\u5982\uff0capplication\/json\uff0ctext\/xml;q=0.9\uff0ctext\/plain;q=0.6 \u6807\u5934\u8868\u793a\u5ba2\u6237\u7aef\u53ef\u4ee5\u63a5\u53d7 JSON\u3001XML \u548c\u7eaf\u6587\u672c\uff0c\u6743\u91cd\u5206\u522b\u4e3a 1.0\u30010.9 \u548c 0.6\u3002JSON \u7684\u6743\u91cd\u4e3a 1.0\uff0c\u56e0\u4e3a\u672a\u63d0\u4f9b\u663e\u5f0f\u6743\u91cd\u3002\u5728\u5185\u5bb9\u534f\u5546\u671f\u95f4\uff0c\u53ef\u4ee5\u4f7f\u7528\u6743\u91cd\u6765\u4e3a\u53cc\u65b9\u9009\u62e9\u6700\u4f73\u8868\u793a\u5f62\u5f0f\u3002<\/p>\n<p>The Content-Type header describes the data sent in a request or response. It contains the MIME type of the data, with an optional character encoding. For example, the application\/json; charset=utf-8 header would indicate that the body of the request or response is JSON, encoded using UTF-8.<br \/>\nContent-Type \u6807\u5934\u63cf\u8ff0\u5728\u8bf7\u6c42\u6216\u54cd\u5e94\u4e2d\u53d1\u9001\u7684\u6570\u636e\u3002\u5b83\u5305\u542b\u6570\u636e\u7684 MIME \u7c7b\u578b\uff0c\u4ee5\u53ca\u53ef\u9009\u7684\u5b57\u7b26\u7f16\u7801\u3002\u4f8b\u5982\uff0capplication\/json;charset=utf-8 \u6807\u5934\u8868\u793a\u8bf7\u6c42\u6216\u54cd\u5e94\u7684\u6b63\u6587\u662f\u4f7f\u7528 UTF-8 \u7f16\u7801\u7684 JSON\u3002<\/p>\n<p>For more on MIME types, see the Mozilla documentation: <a href=\"http:\/\/mng.bz\/gop8\">http:\/\/mng.bz\/gop8<\/a>. You can find the RFC for content negotiation at <a href=\"http:\/\/mng.bz\/6DXo\">http:\/\/mng.bz\/6DXo<\/a>.<br \/>\n\u6709\u5173 MIME \u7c7b\u578b\u7684\u66f4\u591a\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 Mozilla \u6587\u6863\uff1a<a href=\"http:\/\/mng.bz\/gop8\u3002\u60a8\u53ef\u4ee5\u5728\">http:\/\/mng.bz\/gop8\u3002\u60a8\u53ef\u4ee5\u5728<\/a> <a href=\"http:\/\/mng.bz\/6DXo\">http:\/\/mng.bz\/6DXo<\/a> \u4e2d\u627e\u5230\u5185\u5bb9\u534f\u5546\u7684 RFC\u3002<\/p>\n<\/blockquote>\n<p>You\u2019re not forced into sending only a Content-Type the client expects, and in some cases, you may not even be able to handle the types it requests. What if a request stipulates that it can accept only Microsoft Excel spreadsheets? It\u2019s unlikely you\u2019d support that, even if that\u2019s the only Accept type the request contains.<br \/>\n\u4f60\u4e0d\u4f1a\u88ab\u8feb\u53ea\u53d1\u9001\u5ba2\u6237\u7aef\u671f\u671b\u7684 Content-Type\uff0c\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u4f60\u751a\u81f3\u53ef\u80fd\u65e0\u6cd5\u5904\u7406\u5b83\u8bf7\u6c42\u7684\u7c7b\u578b\u3002\u5982\u679c\u8bf7\u6c42\u89c4\u5b9a\u5b83\u53ea\u80fd\u63a5\u53d7 Microsoft Excel \u7535\u5b50\u8868\u683c\uff0c\u8be5\u600e\u4e48\u529e\uff1f\u60a8\u4e0d\u592a\u53ef\u80fd\u652f\u6301\u8fd9\u4e00\u70b9\uff0c\u5373\u4f7f\u8fd9\u662f\u8bf7\u6c42\u5305\u542b\u7684\u552f\u4e00 Accept \u7c7b\u578b\u3002<\/p>\n<p>When you return an API model from an action method, whether directly (as in listing 20.13) or via an OkResult or other StatusCodeResult, ASP.NET Core always returns something in the response. If it can\u2019t honor any of the types stipulated in the Accept header, it will fall back to returning JSON by default. Figure 20.7 shows that even though XML was requested, the API controller formatted the response as JSON.<br \/>\n\u5f53\u4f60\u4ece\u4f5c\u65b9\u6cd5\u8fd4\u56de API \u6a21\u578b\u65f6\uff0c\u65e0\u8bba\u662f\u76f4\u63a5\u8fd4\u56de\uff08\u5982\u6e05\u5355 20.13 \u6240\u793a\uff09\u8fd8\u662f\u901a\u8fc7 OkResult \u6216\u5176\u4ed6 StatusCodeResult\uff0cASP.NET Core \u59cb\u7ec8\u5728\u54cd\u5e94\u4e2d\u8fd4\u56de\u4e00\u4e9b\u5185\u5bb9\u3002\u5982\u679c\u5b83\u4e0d\u80fd\u63a5\u53d7 Accept \u6807\u5934\u4e2d\u89c4\u5b9a\u7684\u4efb\u4f55\u7c7b\u578b\uff0c\u5b83\u5c06\u9ed8\u8ba4\u56de\u9000\u5230\u8fd4\u56de JSON\u3002\u56fe 20.7 \u663e\u793a\uff0c\u5373\u4f7f\u8bf7\u6c42\u4e86 XML\uff0cAPI \u63a7\u5236\u5668\u4e5f\u5c06\u54cd\u5e94\u683c\u5f0f\u5316\u4e3a JSON\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2007.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.7 Even though the request was made with an Accept header of text\/xml, the response returned was JSON, as the server was not configured to return XML.<br \/>\n\u56fe 20.7 \u5373\u4f7f\u8bf7\u6c42\u662f\u4f7f\u7528 text\/xml \u7684 Accept \u6807\u5934\u53d1\u51fa\u7684\uff0c\u8fd4\u56de\u7684\u54cd\u5e94\u4e5f\u662f JSON\uff0c\u56e0\u4e3a\u670d\u52a1\u5668\u672a\u914d\u7f6e\u4e3a\u8fd4\u56de XML\u3002<\/p>\n<p><b>Warning<\/b> In legacy ASP.NET, objects were serialized to JSON using PascalCase, where properties start with a capital letter. In ASP.NET Core, objects are serialized using camelCase by default, where properties start with a lowercase letter.<br \/>\n\u8b66\u544a:\u5728\u65e7\u7248 ASP.NET \u4e2d\uff0c\u5bf9\u8c61\u4f7f\u7528 PascalCase \u5e8f\u5217\u5316\u4e3a JSON\uff0c\u5176\u4e2d\u5c5e\u6027\u4ee5\u5927\u5199\u5b57\u6bcd\u5f00\u5934\u3002\u5728 ASP.NET Core \u4e2d\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\u4f7f\u7528 camelCase \u5e8f\u5217\u5316\u5bf9\u8c61\uff0c\u5176\u4e2d\u5c5e\u6027\u4ee5\u5c0f\u5199\u5b57\u6bcd\u5f00\u5934\u3002<\/p>\n<p>However the data is sent, it\u2019s serialized by an IOutputFormatter implementation. ASP.NET Core ships with a limited number of output formatters out of the box, but as always, it\u2019s easy to add additional ones or change the way the defaults work.<br \/>\n\u65e0\u8bba\u6570\u636e\u5982\u4f55\u53d1\u9001\uff0c\u5b83\u90fd\u4f1a\u7531 IOutputFormatter \u5b9e\u73b0\u8fdb\u884c\u5e8f\u5217\u5316\u3002ASP.NET Core \u9644\u5e26\u4e86\u6709\u9650\u6570\u91cf\u7684\u5f00\u7bb1\u5373\u7528\u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f\uff0c\u4f46\u4e0e\u5f80\u5e38\u4e00\u6837\uff0c\u6dfb\u52a0\u5176\u4ed6\u683c\u5f0f\u5316\u7a0b\u5e8f\u6216\u66f4\u6539\u9ed8\u8ba4\u503c\u7684\u5de5\u4f5c\u65b9\u5f0f\u5f88\u5bb9\u6613\u3002<\/p>\n<h3>20.5.1 Customizing the default formatters: Adding XML support<\/h3>\n<p>20.5.1 \u81ea\u5b9a\u4e49\u9ed8\u8ba4\u683c\u5f0f\u5316\u7a0b\u5e8f\uff1a\u6dfb\u52a0 XML \u652f\u6301<\/p>\n<p>As with most of ASP.NET Core, the Web API formatters are completely customizable. By default, only formatters for plain text (text\/plain), HTML (text\/html), and JSON (application\/json) are configured. Given the common use case of single-page application (SPAs) and mobile applications, this will get you a long way. But sometimes you need to be able to return data in a different format, such as XML.<br \/>\n\u4e0e\u5927\u591a\u6570 ASP.NET Core \u4e00\u6837\uff0cWeb API \u683c\u5f0f\u5316\u7a0b\u5e8f\u662f\u5b8c\u5168\u53ef\u81ea\u5b9a\u4e49\u7684\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u4ec5\u914d\u7f6e\u7eaf\u6587\u672c \uff08text\/plain\uff09\u3001HTML \uff08text\/html\uff09 \u548c JSON \uff08application\/json\uff09 \u7684\u683c\u5f0f\u5316\u7a0b\u5e8f\u3002\u9274\u4e8e\u5355\u9875\u5e94\u7528\u7a0b\u5e8f \uff08SPA\uff09 \u548c\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\u7684\u5e38\u89c1\u7528\u4f8b\uff0c\u8fd9\u5c06\u4f7f\u60a8\u5927\u6709\u5e2e\u52a9\u3002\u4f46\u6709\u65f6\u60a8\u9700\u8981\u80fd\u591f\u4ee5\u4e0d\u540c\u7684\u683c\u5f0f\uff08\u5982 XML\uff09\u8fd4\u56de\u6570\u636e\u3002<\/p>\n<blockquote>\n<p>Newtonsoft.Json vs. System.Text.Json<\/p>\n<p>Newtonsoft.Json, also known as Json.NET, has for a long time been the canonical way to work with JSON in .NET. It\u2019s compatible with every version of .NET under the sun, and it will no doubt be familiar to virtually all .NET developers. Its reach was so great that even ASP.NET Core took a dependency on it!<br \/>\nNewtonsoft.Json\uff0c\u4e5f\u79f0\u4e3a Json.NET\uff0c\u957f\u671f\u4ee5\u6765\u4e00\u76f4\u662f\u5728 .NET \u4e2d\u4f7f\u7528 JSON \u7684\u89c4\u8303\u65b9\u5f0f\u3002\u5b83\u4e0e\u5168\u7403\u6240\u6709\u7248\u672c\u7684 .NET \u517c\u5bb9\uff0c\u6beb\u65e0\u7591\u95ee\uff0c\u51e0\u4e4e\u6240\u6709 .NET \u5f00\u53d1\u4eba\u5458\u90fd\u719f\u6089\u5b83\u3002\u5b83\u7684\u8986\u76d6\u8303\u56f4\u5982\u6b64\u4e4b\u5927\uff0c\u4ee5\u81f3\u4e8e ASP.NET Core \u90fd\u4f9d\u8d56\u4e8e\u5b83\uff01<\/p>\n<p>That all changed with the introduction of a new library in ASP.NET Core 3.0, System .Text.Json, which focuses on performance. In .NET Core 3.0 onward, ASP.NET Core uses System.Text.Json by default instead of Newtonsoft.Json.<br \/>\n\u968f\u7740 ASP.NET Core 3.0 \u4e2d\u5f15\u5165\u65b0\u5e93 System \uff0c\u8fd9\u4e00\u5207\u90fd\u53d1\u751f\u4e86\u53d8\u5316\u3002Text.Json\uff0c\u5b83\u4fa7\u91cd\u4e8e\u6027\u80fd\u3002\u5728 .NET Core 3.0 \u53ca\u66f4\u9ad8\u7248\u672c\u4e2d\uff0cASP.NET Core \u9ed8\u8ba4\u4f7f\u7528 System.Text.Json\uff0c\u800c\u4e0d\u662f Newtonsoft.Json\u3002<\/p>\n<p>The main difference between the libraries is that System.Text.Json is picky about its JSON. It will generally only deserialize JSON that matches its expectations. For example, System.Text.Json won\u2019t deserialize JSON that uses single quotes around strings; you have to use double quotes.<br \/>\n\u8fd9\u4e24\u4e2a\u5e93\u4e4b\u95f4\u7684\u4e3b\u8981\u533a\u522b\u5728\u4e8e System.Text.Json \u5bf9\u5176 JSON \u5f88\u6311\u5254\u3002\u5b83\u901a\u5e38\u53ea\u4f1a\u53cd\u5e8f\u5217\u5316\u7b26\u5408\u5176\u9884\u671f\u7684 JSON\u3002\u4f8b\u5982\uff0cSystem.Text.Json \u4e0d\u4f1a\u53cd\u5e8f\u5217\u5316\u5728\u5b57\u7b26\u4e32\u5468\u56f4\u4f7f\u7528\u5355\u5f15\u53f7\u7684 JSON;\u60a8\u5fc5\u987b\u4f7f\u7528\u53cc\u5f15\u53f7\u3002<\/p>\n<p>If you\u2019re creating a new application, this is generally not a problem; you quickly learn to generate the correct JSON. But if you\u2019re converting an application to ASP.NET Core or are sending JSON to a third party you don\u2019t control, these limitations can be real stumbling blocks.<br \/>\n\u5982\u679c\u8981\u521b\u5efa\u65b0\u5e94\u7528\u7a0b\u5e8f\uff0c\u8fd9\u901a\u5e38\u4e0d\u662f\u95ee\u9898;\u60a8\u5f88\u5feb\u5c31\u5b66\u4f1a\u4e86\u751f\u6210\u6b63\u786e\u7684 JSON\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u8981\u5c06\u5e94\u7528\u7a0b\u5e8f\u8f6c\u6362\u4e3a ASP.NET Core \u6216\u5c06 JSON \u53d1\u9001\u7ed9\u60a8\u65e0\u6cd5\u63a7\u5236\u7684\u7b2c\u4e09\u65b9\uff0c\u5219\u8fd9\u4e9b\u9650\u5236\u53ef\u80fd\u4f1a\u6210\u4e3a\u771f\u6b63\u7684\u7eca\u811a\u77f3\u3002<\/p>\n<p>Luckily, you can easily switch back to the Newtonsoft.Json library instead. Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson package into your project and update the AddControllers() method in Program.cs to the following:<br \/>\n\u5e78\u8fd0\u7684\u662f\uff0c\u60a8\u53ef\u4ee5\u8f7b\u677e\u5730\u5207\u6362\u56de Newtonsoft.Json \u5e93\u3002\u5c06 Microsoft.AspNetCore.Mvc.NewtonsoftJson \u5305\u5b89\u88c5\u5230\u9879\u76ee\u4e2d\uff0c\u5e76\u5c06 Program.cs \u4e2d\u7684 AddControllers\uff08\uff09 \u65b9\u6cd5\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<\/blockquote>\n<pre><code>builder.Services.AddControllers()\n    .AddNewtonsoftJson();<\/code><\/pre>\n<blockquote>\n<p>This will switch ASP.NET Core\u2019s formatters to use Newtonsoft.Json behind the scenes, instead of System.Text.Json. For more details on the differences between the libraries, see Microsoft\u2019s article \u201cCompare Newtonsoft.Json to System.Text.Json, and migrate to System.Text.Json\u201d: <a href=\"http:\/\/mng.bz\/0mRJ\">http:\/\/mng.bz\/0mRJ<\/a>. For more advice on when to switch to the Newtonsoft.Json formatter, see the section \u201cAdd Newtonsoft.Json-based JSON format support\u201d in Microsoft\u2019s \u201cFormat response data in ASP.NET Core Web API\u201d documentation: <a href=\"http:\/\/mng.bz\/zx11\">http:\/\/mng.bz\/zx11<\/a>.<br \/>\n\u8fd9\u4f1a\u5c06 ASP.NET Core \u7684\u683c\u5f0f\u5316\u7a0b\u5e8f\u5207\u6362\u4e3a\u5728\u540e\u53f0\u4f7f\u7528 Newtonsoft.Json\uff0c\u800c\u4e0d\u662f System.Text.Json\u3002\u6709\u5173\u5e93\u4e4b\u95f4\u5dee\u5f02\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 Microsoft \u7684\u6587\u7ae0\u201c\u5c06 Newtonsoft.Json \u4e0e System.Text.Json \u8fdb\u884c\u6bd4\u8f83\uff0c\u5e76\u8fc1\u79fb\u5230 System.Text.Json\u201d\uff1a<a href=\"http:\/\/mng.bz\/0mRJ\u3002\u6709\u5173\u4f55\u65f6\u5207\u6362\u5230\">http:\/\/mng.bz\/0mRJ\u3002\u6709\u5173\u4f55\u65f6\u5207\u6362\u5230<\/a> Newtonsoft.Json \u683c\u5f0f\u5316\u7a0b\u5e8f\u7684\u66f4\u591a\u5efa\u8bae\uff0c\u8bf7\u53c2\u9605 Microsoft \u7684\u201c\u5728 ASP.NET Core Web API \u4e2d\u683c\u5f0f\u5316\u54cd\u5e94\u6570\u636e\u201d\u6587\u6863\u4e2d\u7684\u201c\u6dfb\u52a0\u57fa\u4e8e Newtonsoft.Json \u7684 JSON \u683c\u5f0f\u652f\u6301\u201d\u90e8\u5206\uff1a<a href=\"http:\/\/mng.bz\/zx11\">http:\/\/mng.bz\/zx11<\/a>\u3002<\/p>\n<\/blockquote>\n<p>You can add XML output to your application by adding an output formatter. You configure your application\u2019s formatters in Program.cs by customizing the IMvcBuilder object returned from AddControllers(). To add the XML output formatter, use the following:<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u6dfb\u52a0\u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f\u5c06 XML \u8f93\u51fa\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u3002\u901a\u8fc7\u81ea\u5b9a\u4e49\u4ece AddControllers\uff08\uff09 \u8fd4\u56de\u7684 IMvcBuilder \u5bf9\u8c61\uff0c\u53ef\u4ee5\u5728 Program.cs \u4e2d\u914d\u7f6e\u5e94\u7528\u7a0b\u5e8f\u7684\u683c\u5f0f\u5316\u7a0b\u5e8f\u3002\u8981\u6dfb\u52a0 XML \u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f\uff0c\u8bf7\u4f7f\u7528\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n<pre><code>services.AddControllers()\n    .AddXmlSerializerFormatters();<\/code><\/pre>\n<p><b>NOTE<\/b> Technically, this also adds an XML input formatter, which means your application can now receive XML in requests too. Previously, sending a request with XML in the body would respond with a 415 Unsupported Media Type response. For a detailed look at formatters, including creating a custom formatter, see the documentation at <a href=\"http:\/\/mng.bz\/e5gG\">http:\/\/mng.bz\/e5gG<\/a>.<br \/>\n\u6ce8\u610f:\u4ece\u6280\u672f\u4e0a\u8bb2\uff0c\u8fd9\u8fd8\u6dfb\u52a0\u4e86\u4e00\u4e2a XML \u8f93\u5165\u683c\u5f0f\u5316\u7a0b\u5e8f\uff0c\u8fd9\u610f\u5473\u7740\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u73b0\u5728\u4e5f\u53ef\u4ee5\u5728\u8bf7\u6c42\u4e2d\u63a5\u6536 XML\u3002\u4ee5\u524d\uff0c\u53d1\u9001\u6b63\u6587\u4e2d\u5305\u542b XML \u7684\u8bf7\u6c42\u5c06\u54cd\u5e94 415 Unsupported Media Type \u54cd\u5e94\u3002\u6709\u5173\u683c\u5f0f\u5316\u7a0b\u5e8f\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u5305\u62ec\u521b\u5efa\u81ea\u5b9a\u4e49\u683c\u5f0f\u5316\u7a0b\u5e8f\uff0c\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/e5gG\">http:\/\/mng.bz\/e5gG<\/a> \u4e2d\u7684\u6587\u6863\u3002<\/p>\n<p>With this simple change, your API controllers can now format responses as XML as well as JSON. Running the same request as shown in figure 20.7 with XML support enabled means the app will respect the text\/xml accept header. The formatter serializes the string array to XML as requested instead of defaulting to JSON, as shown in figure 20.8.<br \/>\n\u901a\u8fc7\u8fd9\u4e2a\u7b80\u5355\u7684\u66f4\u6539\uff0c\u60a8\u7684 API \u63a7\u5236\u5668\u73b0\u5728\u53ef\u4ee5\u5c06\u54cd\u5e94\u683c\u5f0f\u5316\u4e3a XML \u548c JSON\u3002\u5728\u542f\u7528 XML \u652f\u6301\u7684\u60c5\u51b5\u4e0b\u8fd0\u884c\u5982\u56fe 20.7 \u6240\u793a\u7684\u76f8\u540c\u8bf7\u6c42\u610f\u5473\u7740\u5e94\u7528\u7a0b\u5e8f\u5c06\u9075\u5faa text\/xml accept \u6807\u5934\u3002\u683c\u5f0f\u5316\u7a0b\u5e8f\u6839\u636e\u8bf7\u6c42\u5c06\u5b57\u7b26\u4e32\u6570\u7ec4\u5e8f\u5217\u5316\u4e3a XML\uff0c\u800c\u4e0d\u662f\u9ed8\u8ba4\u4e3a JSON\uff0c\u5982\u56fe 20.8 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2008.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 20.8 With the XML output formatters added, the Accept header\u2019 text\/xml value is respected, and the response is serialized to XML.<br \/>\n\u56fe 20.8 \u6dfb\u52a0 XML \u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f\u540e\uff0c\u5c06\u9075\u5faa Accept \u6807\u5934\u7684 text\/xml \u503c\uff0c\u5e76\u5c06\u54cd\u5e94\u5e8f\u5217\u5316\u4e3a XML\u3002<\/p>\n<p>This is an example of content negotiation, where the client has specified which formats it can handle and the server selects one of those, based on what it can produce. This approach is part of the HTTP protocol, but there are some quirks to be aware of when relying on it in ASP.NET Core. You won\u2019t often run into these, but if you\u2019re not aware of them when they hit you, they could have you scratching your head for hours!<br \/>\n\u8fd9\u662f\u5185\u5bb9\u534f\u5546\u7684\u4e00\u4e2a\u793a\u4f8b\uff0c\u5176\u4e2d\u5ba2\u6237\u7aef\u6307\u5b9a\u4e86\u5b83\u53ef\u4ee5\u5904\u7406\u7684\u683c\u5f0f\uff0c\u670d\u52a1\u5668\u6839\u636e\u5b83\u53ef\u4ee5\u751f\u6210\u7684\u683c\u5f0f\u9009\u62e9\u5176\u4e2d\u4e00\u79cd\u3002\u6b64\u65b9\u6cd5\u662f HTTP \u534f\u8bae\u7684\u4e00\u90e8\u5206\uff0c\u4f46\u5728 ASP.NET Core \u4e2d\u4f9d\u8d56\u5b83\u65f6\uff0c\u9700\u8981\u6ce8\u610f\u4e00\u4e9b\u602a\u7656\u3002\u4f60\u4e0d\u4f1a\u7ecf\u5e38\u9047\u5230\u8fd9\u4e9b\uff0c\u4f46\u5982\u679c\u5b83\u4eec\u5728\u5b83\u4eec\u51fb\u4e2d\u4f60\u65f6\u6ca1\u6709\u610f\u8bc6\u5230\u5b83\u4eec\uff0c\u5b83\u4eec\u53ef\u80fd\u4f1a\u8ba9\u4f60\u6320\u5934\u51e0\u4e2a\u5c0f\u65f6\uff01<\/p>\n<h3>20.5.2 Choosing a response format with content negotiation<\/h3>\n<p>20.5.2 \u4f7f\u7528\u5185\u5bb9\u534f\u5546\u9009\u62e9\u54cd\u5e94\u683c\u5f0f<\/p>\n<p>Content negotiation is where a client says which types of data it can accept using the Accept header and the server picks the best one it can handle. Generally speaking, this works as you\u2019d hope: the server formats the data using a type the client can understand.<br \/>\n\u5185\u5bb9\u534f\u5546\u662f\u5ba2\u6237\u7aef\u4f7f\u7528 Accept \u6807\u5934\u8bf4\u660e\u5b83\u53ef\u4ee5\u63a5\u53d7\u54ea\u4e9b\u7c7b\u578b\u7684\u6570\u636e\uff0c\u670d\u52a1\u5668\u9009\u62e9\u5b83\u53ef\u4ee5\u5904\u7406\u7684\u6700\u4f73\u6570\u636e\u7c7b\u578b\u3002\u4e00\u822c\u6765\u8bf4\uff0c\u8fd9\u5c31\u50cf\u4f60\u5e0c\u671b\u7684\u90a3\u6837\u5de5\u4f5c\uff1a\u670d\u52a1\u5668\u4f7f\u7528\u5ba2\u6237\u7aef\u53ef\u4ee5\u7406\u89e3\u7684\u7c7b\u578b\u6765\u683c\u5f0f\u5316\u6570\u636e\u3002<\/p>\n<p>The ASP.NET Core implementation has some special cases that are worth bearing in mind:<br \/>\nASP.NET Core \u5b9e\u73b0\u6709\u4e00\u4e9b\u503c\u5f97\u7262\u8bb0\u7684\u7279\u6b8a\u60c5\u51b5\uff1a<\/p>\n<p>\u2022  By default, ASP.NET Core returns only application\/json, text\/plain, and text\/html MIME types. You can add IOutputFormatters to make other types available, as you saw in the previous section for text\/xml.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cASP.NET Core \u4ec5\u8fd4\u56de application\/json\u3001text\/plain \u548c text\/html MIME \u7c7b\u578b\u3002\u60a8\u53ef\u4ee5\u6dfb\u52a0 IOutputFormatters \u4ee5\u4f7f\u5176\u4ed6\u7c7b\u578b\u53ef\u7528\uff0c\u5982\u4e0a\u4e00\u8282 text\/xml \u4e2d\u6240\u793a\u3002<br \/>\n\u2022  By default, if you return null as your API model, whether from an action method or by passing null in a StatusCodeResult, the middleware returns a 204 No Content response.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u5982\u679c\u8fd4\u56de null \u4f5c\u4e3a API \u6a21\u578b \uff08\u65e0\u8bba\u662f\u4ece\u4f5c\u65b9\u6cd5\u8fd8\u662f\u901a\u8fc7\u5728 StatusCodeResult \u4e2d\u4f20\u9012 null\uff09\uff0c\u4e2d\u95f4\u4ef6\u90fd\u4f1a\u8fd4\u56de 204 No Content \u54cd\u5e94\u3002<br \/>\n\u2022  When you return a string as your API model, if no Accept header is set, ASP.NET Core formats the response as text\/plain.<br \/>\n\u5f53\u60a8\u5c06\u5b57\u7b26\u4e32\u4f5c\u4e3a API \u6a21\u578b\u8fd4\u56de\u65f6\uff0c\u5982\u679c\u672a\u8bbe\u7f6e Accept \u6807\u5934\uff0c\u5219 ASP.NET Core \u4f1a\u5c06\u54cd\u5e94\u683c\u5f0f\u8bbe\u7f6e\u4e3a text\/plain\u3002<br \/>\n\u2022  When you use any other class as your API model, and there\u2019s no Accept header or none of the supported formats was requested, the first formatter that can generate a response is used (typically JSON by default).<br \/>\n\u5f53\u60a8\u4f7f\u7528\u4efb\u4f55\u5176\u4ed6\u7c7b\u4f5c\u4e3a API \u6a21\u578b\uff0c\u5e76\u4e14\u6ca1\u6709 Accept \u6807\u5934\u6216\u672a\u8bf7\u6c42\u4efb\u4f55\u652f\u6301\u7684\u683c\u5f0f\u65f6\uff0c\u5c06\u4f7f\u7528\u7b2c\u4e00\u4e2a\u53ef\u4ee5\u751f\u6210\u54cd\u5e94\u7684\u683c\u5f0f\u5316\u7a0b\u5e8f \uff08\u901a\u5e38\u9ed8\u8ba4\u4e3a JSON\uff09\u3002<br \/>\n\u2022  If the middleware detects that the request is probably from a browser (the accept header contains <em>\/<\/em>), it will not use conneg. Instead, it formats the response as though an Accept header was not provided, using the default formatter (typically JSON).<br \/>\n\u5982\u679c\u4e2d\u95f4\u4ef6\u68c0\u6d4b\u5230\u8bf7\u6c42\u53ef\u80fd\u6765\u81ea\u6d4f\u89c8\u5668 \uff08accept \u6807\u5934\u5305\u542b \/\uff09 \uff0c\u5219\u4e0d\u4f1a\u4f7f\u7528 conneg\u3002\u76f8\u53cd\uff0c\u5b83\u4f7f\u7528\u9ed8\u8ba4\u683c\u5f0f\u5316\u7a0b\u5e8f\uff08\u901a\u5e38\u4e3a JSON\uff09\u683c\u5f0f\u5316\u54cd\u5e94\uff0c\u5c31\u50cf\u672a\u63d0\u4f9b Accept \u6807\u5934\u4e00\u6837\u3002<\/p>\n<p>These defaults are relatively sane, but they can certainly bite you if you\u2019re not aware of them. That last point in particular, where the response to a request from a browser is virtually always formatted as JSON, has certainly caught me out when trying to test XML requests locally!<br \/>\n\u8fd9\u4e9b\u8fdd\u7ea6\u76f8\u5bf9\u6765\u8bf4\u662f\u7406\u667a\u7684\uff0c\u4f46\u5982\u679c\u4f60\u4e0d\u77e5\u9053\u5b83\u4eec\uff0c\u5b83\u4eec\u80af\u5b9a\u4f1a\u54ac\u4f60\u4e00\u53e3\u3002\u7279\u522b\u662f\u6700\u540e\u4e00\u70b9\uff0c\u5bf9\u6765\u81ea\u6d4f\u89c8\u5668\u7684\u8bf7\u6c42\u7684\u54cd\u5e94\u51e0\u4e4e\u603b\u662f\u683c\u5f0f\u5316\u4e3a JSON\uff0c\u5728\u5c1d\u8bd5\u5728\u672c\u5730\u6d4b\u8bd5 XML \u8bf7\u6c42\u65f6\uff0c\u8fd9\u65e0\u7591\u8ba9\u6211\u611f\u5230\u56f0\u60d1\uff01<\/p>\n<p>As you should expect by now, all these rules are configurable; you can easily change the default behavior in your application if it doesn\u2019t fit your requirements. For example, the following listing, taken from Program.cs, shows how you can force the middleware to respect the browser\u2019s Accept header and remove the text\/plain formatter for strings.<br \/>\n\u6b63\u5982\u60a8\u73b0\u5728\u5e94\u8be5\u9884\u6599\u7684\u90a3\u6837\uff0c\u6240\u6709\u8fd9\u4e9b\u89c4\u5219\u90fd\u662f\u53ef\u914d\u7f6e\u7684;\u5982\u679c\u5e94\u7528\u7a0b\u5e8f\u7684\u9ed8\u8ba4\u884c\u4e3a\u4e0d\u7b26\u5408\u60a8\u7684\u8981\u6c42\uff0c\u60a8\u53ef\u4ee5\u8f7b\u677e\u66f4\u6539\u5e94\u7528\u7a0b\u5e8f\u7684\u9ed8\u8ba4\u884c\u4e3a\u3002\u4f8b\u5982\uff0c\u4ee5\u4e0b\u6e05\u5355\u53d6\u81ea Program.cs\uff0c\u5c55\u793a\u4e86\u5982\u4f55\u5f3a\u5236\u4e2d\u95f4\u4ef6\u9075\u5b88\u6d4f\u89c8\u5668\u7684 Accept \u6807\u5934\u5e76\u5220\u9664\u5b57\u7b26\u4e32\u7684\u6587\u672c\/\u7eaf\u683c\u5f0f\u5316\u7a0b\u5e8f\u3002<\/p>\n<p>Listing 20.14 Customizing MVC to respect the browser\u2019s Accept header in web APIs<br \/>\n\u6e05\u5355 20.14 \u5728 Web API \u4e2d\u81ea\u5b9a\u4e49 MVC \u4ee5\u9075\u5faa\u6d4f\u89c8\u5668\u7684 Accept \u6807\u5934<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers(options =&gt;      #A\n{\n    options.RespectBrowserAcceptHeader = true;         #B\n    options.OutputFormatters.RemoveType&lt;StringOutputFormatter&gt;();   #C\n});<\/code><\/pre>\n<p>\u2776 AddControllers has an overload that takes a lambda function.<br \/>\nAddControllers \u5177\u6709\u91c7\u7528 lambda \u51fd\u6570\u7684\u91cd\u8f7d\u3002<br \/>\n\u2777 False by default; several other properties are also available to be set.<br \/>\n\u9ed8\u8ba4\u4e3a False;\u8fd8\u53ef\u4ee5\u8bbe\u7f6e\u5176\u4ed6\u51e0\u4e2a\u5c5e\u6027\u3002<br \/>\n\u2778 Removes the output formatter that formats strings as text\/plain<br \/>\n\u5220\u9664\u5c06\u5b57\u7b26\u4e32\u683c\u5f0f\u5316\u4e3a text\/plain \u7684\u8f93\u51fa\u683c\u5f0f\u5316\u7a0b\u5e8f<\/p>\n<p>In most cases, conneg should work well for you out of the box, whether you\u2019re building an SPA or a mobile application. In some cases, you may find you need to bypass the usual conneg mechanisms for specific action methods, and there are various ways to achieve this, but I won\u2019t cover them in this book as I\u2019ve found I rarely need to use them. For details, see Microsoft\u2019s \u201cFormat response data in ASP.NET Core Web API\u201d documentation: <a href=\"http:\/\/mng.bz\/zx11\">http:\/\/mng.bz\/zx11<\/a>.<br \/>\n\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0cconneg \u5e94\u8be5\u53ef\u4ee5\u7acb\u5373\u4e3a\u60a8\u5de5\u4f5c\uff0c\u65e0\u8bba\u60a8\u662f\u6784\u5efa SPA \u8fd8\u662f\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\u3002\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u4f60\u53ef\u80fd\u4f1a\u53d1\u73b0\u4f60\u9700\u8981\u7ed5\u8fc7\u7279\u5b9a\u52a8\u4f5c\u65b9\u6cd5\u7684\u901a\u5e38\u8fde\u63a5\u673a\u5236\uff0c\u5e76\u4e14\u6709\u591a\u79cd\u65b9\u6cd5\u53ef\u4ee5\u5b9e\u73b0\u8fd9\u4e00\u70b9\uff0c\u4f46\u6211\u4e0d\u4f1a\u5728\u672c\u4e66\u4e2d\u4ecb\u7ecd\u5b83\u4eec\uff0c\u56e0\u4e3a\u6211\u53d1\u73b0\u6211\u5f88\u5c11\u9700\u8981\u4f7f\u7528\u5b83\u4eec\u3002\u6709\u5173\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 Microsoft \u7684\u201c\u5728 ASP.NET Core Web API \u4e2d\u8bbe\u7f6e\u54cd\u5e94\u6570\u636e\u7684\u683c\u5f0f\u201d\u6587\u6863\uff1a<a href=\"http:\/\/mng.bz\/zx11\">http:\/\/mng.bz\/zx11<\/a>\u3002<\/p>\n<p>At this point we\u2019ve covered the main points of using API controllers, but you probably still have one major question: why would I use web API controllers over minimal APIs? That\u2019s a great question, and one we\u2019ll look at in section 20.6.<br \/>\n\u5728\u8fd9\u4e00\u70b9\u4e0a\uff0c\u6211\u4eec\u5df2\u7ecf\u4ecb\u7ecd\u4e86\u4f7f\u7528 API \u63a7\u5236\u5668\u7684\u8981\u70b9\uff0c\u4f46\u60a8\u53ef\u80fd\u4ecd\u7136\u6709\u4e00\u4e2a\u4e3b\u8981\u95ee\u9898\uff1a\u4e3a\u4ec0\u4e48\u6211\u8981\u4f7f\u7528 Web API \u63a7\u5236\u5668\u800c\u4e0d\u662f\u6700\u5c0f\u7684 API\uff1f\u8fd9\u662f\u4e00\u4e2a\u5f88\u597d\u7684\u95ee\u9898\uff0c\u6211\u4eec\u5c06\u5728 20.6 \u8282\u4e2d\u8ba8\u8bba\u8fd9\u4e2a\u95ee\u9898\u3002<\/p>\n<h2>20.6 Choosing between web API controllers and minimal APIs<\/h2>\n<p>20.6 \u5728 Web API \u63a7\u5236\u5668\u548c\u6700\u5c0f API \u4e4b\u95f4\u8fdb\u884c\u9009\u62e9<\/p>\n<p>In part 1 of this book you learned all about using minimal APIs to build a JSON API. Minimal APIs are the new kid on the block, being introduced in .NET 6, but they are growing up quickly. With all the new features introduced in .NET 7 (discussed in chapter 5), minimal APIs are emerging as a great way to build HTTP APIs in modern .NET.<br \/>\n\u5728\u672c\u4e66\u7684\u7b2c 1 \u90e8\u5206\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u5982\u4f55\u4f7f\u7528\u6700\u5c0f API \u6784\u5efa JSON API \u7684\u6240\u6709\u4fe1\u606f\u3002\u6700\u5c0f API \u662f .NET 6 \u4e2d\u5f15\u5165\u7684\u65b0\u6210\u5458\uff0c\u4f46\u5b83\u4eec\u6b63\u5728\u8fc5\u901f\u53d1\u5c55\u3002\u968f\u7740 .NET 7 \u4e2d\u5f15\u5165\u7684\u6240\u6709\u65b0\u529f\u80fd\uff08\u5728\u7b2c 5 \u7ae0\u4e2d\u8ba8\u8bba\uff09\uff0c\u6700\u5c0f API \u6b63\u5728\u6210\u4e3a\u5728\u73b0\u4ee3 .NET \u4e2d\u6784\u5efa HTTP API \u7684\u597d\u65b9\u6cd5\u3002<\/p>\n<p>By contrast, web API controllers have been around since day one. They were introduced in their current form in ASP.NET Core 1.0 and were heavily inspired by the web API framework from legacy ASP.NET. The designs, patterns, and concepts used by web API controllers haven\u2019t changed much since then, so if you\u2019ve ever used web API controllers, they should look familiar in .NET 7.<br \/>\n\u76f8\u6bd4\u4e4b\u4e0b\uff0cWeb API \u63a7\u5236\u5668\u4ece\u7b2c\u4e00\u5929\u8d77\u5c31\u5df2\u7ecf\u5b58\u5728\u3002\u5b83\u4eec\u662f\u5728 ASP.NET Core 1.0 \u4e2d\u4ee5\u5f53\u524d\u5f62\u5f0f\u5f15\u5165\u7684\uff0c\u5e76\u53d7\u5230\u65e7\u7248 ASP.NET \u7684 Web API \u6846\u67b6\u7684\u4e25\u91cd\u542f\u53d1\u3002\u4ece\u90a3\u65f6\u8d77\uff0cWeb API \u63a7\u5236\u5668\u4f7f\u7528\u7684\u8bbe\u8ba1\u3001\u6a21\u5f0f\u548c\u6982\u5ff5\u6ca1\u6709\u592a\u5927\u53d8\u5316\uff0c\u56e0\u6b64\uff0c\u5982\u679c\u60a8\u66fe\u7ecf\u4f7f\u7528\u8fc7 Web API \u63a7\u5236\u5668\uff0c\u5b83\u4eec\u5728 .NET 7 \u4e2d\u5e94\u8be5\u770b\u8d77\u6765\u5f88\u719f\u6089\u3002<\/p>\n<p>The difficult question in .NET 7 is if you need to build an API, which should you use, minimal APIs or web API controllers? Both have their pros and cons, and a large part of the decision will be personal preference, but to help your decision, you should ask yourself several questions:<br \/>\n.NET 7 \u4e2d\u7684\u96be\u9898\u662f\uff0c\u60a8\u662f\u5426\u9700\u8981\u6784\u5efa\u4e00\u4e2a API\uff0c\u60a8\u5e94\u8be5\u4f7f\u7528\u54ea\u4e2a API\uff0c\u6700\u5c0f API \u8fd8\u662f Web API \u63a7\u5236\u5668\uff1f\u4e24\u8005\u90fd\u6709\u5176\u4f18\u70b9\u548c\u7f3a\u70b9\uff0c\u5f88\u5927\u4e00\u90e8\u5206\u51b3\u5b9a\u5c06\u662f\u4e2a\u4eba\u559c\u597d\uff0c\u4f46\u4e3a\u4e86\u5e2e\u52a9\u4f60\u505a\u51fa\u51b3\u5b9a\uff0c\u4f60\u5e94\u8be5\u95ee\u81ea\u5df1\u51e0\u4e2a\u95ee\u9898\uff1a<\/p>\n<ol>\n<li>Do you need to return data in multiple formats using content negotiation?<br \/>\n\u60a8\u662f\u5426\u9700\u8981\u4f7f\u7528 Content Negotiation \u4ee5\u591a\u79cd\u683c\u5f0f\u8fd4\u56de\u6570\u636e\uff1f<\/li>\n<li>Is performance critical to your application?<br \/>\n\u6027\u80fd\u5bf9\u5e94\u7528\u7a0b\u5e8f\u81f3\u5173\u91cd\u8981\u5417\uff1f<\/li>\n<li>Do you have complex filtering requirements?<br \/>\n\u60a8\u662f\u5426\u6709\u590d\u6742\u7684\u7b5b\u9009\u8981\u6c42\uff1f<\/li>\n<li>Is this a new project?<br \/>\n\u8fd9\u662f\u4e00\u4e2a\u65b0\u9879\u76ee\u5417\uff1f<\/li>\n<li>Do you already have experience with web API controllers?<br \/>\n\u60a8\u662f\u5426\u5df2\u7ecf\u6709\u4f7f\u7528 Web API \u63a7\u5236\u5668\u7684\u7ecf\u9a8c\uff1f<\/li>\n<li>Do you prefer convention over configuration?<br \/>\n\u60a8\u66f4\u559c\u6b22\u7ea6\u5b9a\u800c\u4e0d\u662f\u914d\u7f6e\u5417\uff1f<\/li>\n<\/ol>\n<p>Questions 1-3 in this list are focused on technical differences between minimal APIs and web API controllers. Web API controllers support content negotiation (conneg), which allows clients to request data be returned in a particular format: JSON, XML, or CSV, for example, as you learned in section 20.5. Web API controllers support this feature out of the box, so if it\u2019s crucial for your application, it may be better to choose web API controllers over minimal APIs.<br \/>\n\u6b64\u5217\u8868\u4e2d\u7684\u95ee\u9898 1-3 \u4fa7\u91cd\u4e8e\u6700\u5c0f API \u548c Web API \u63a7\u5236\u5668\u4e4b\u95f4\u7684\u6280\u672f\u5dee\u5f02\u3002Web API \u63a7\u5236\u5668\u652f\u6301\u5185\u5bb9\u534f\u5546 \uff08conneg\uff09\uff0c\u5b83\u5141\u8bb8 Client \u7aef\u8bf7\u6c42\u4ee5\u7279\u5b9a\u683c\u5f0f\u8fd4\u56de\u6570\u636e\uff1aJSON\u3001XML \u6216 CSV\uff0c\u4f8b\u5982\uff0c\u5982\u60a8\u5728\u7b2c 20.5 \u8282\u4e2d\u5b66\u5230\u7684\u90a3\u6837\u3002Web API \u63a7\u5236\u5668\u652f\u6301\u5f00\u7bb1\u5373\u7528\u7684\u6b64\u529f\u80fd\uff0c\u56e0\u6b64\u5982\u679c\u5b83\u5bf9\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u81f3\u5173\u91cd\u8981\uff0c\u6700\u597d\u9009\u62e9 Web API \u63a7\u5236\u5668\u800c\u4e0d\u662f\u6700\u5c11\u7684 API\u3002<\/p>\n<p><b>Tip<\/b> If you want to use content negotiation with minimal APIs, it\u2019s possible but not built in. I show how to add conneg to minimal APIs using the open-source library Carter on my blog: <a href=\"http:\/\/mng.bz\/o12d\">http:\/\/mng.bz\/o12d<\/a>.<br \/>\n\u63d0\u793a:\u5982\u679c\u60a8\u60f3\u901a\u8fc7\u6700\u5c11\u7684 API \u4f7f\u7528\u5185\u5bb9\u534f\u5546\uff0c\u8fd9\u662f\u53ef\u80fd\u7684\uff0c\u4f46\u4e0d\u662f\u5185\u7f6e\u7684\u3002\u6211\u5c55\u793a\u4e86\u5982\u4f55\u4f7f\u7528\u5f00\u6e90\u5e93 Carter \u5c06 conneg \u6dfb\u52a0\u5230\u6700\u5c0f\u7684 API\uff1a<a href=\"http:\/\/mng.bz\/o12d\">http:\/\/mng.bz\/o12d<\/a>\u3002<\/p>\n<p>Question 2 is about performance. Everyone wants the most performant app, but there\u2019s a real question of how important it is. Are you going to be regularly benchmarking your application and looking for any regressions? If so, minimal APIs are probably going to be a better choice, as they\u2019re often more performant than web API controllers.<br \/>\n\u95ee\u9898 2 \u662f\u5173\u4e8e\u6027\u80fd\u7684\u3002\u6bcf\u4e2a\u4eba\u90fd\u5e0c\u671b\u83b7\u5f97\u6027\u80fd\u6700\u9ad8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f46\u5b58\u5728\u4e00\u4e2a\u771f\u6b63\u7684\u95ee\u9898\uff0c\u5373\u5b83\u6709\u591a\u91cd\u8981\u3002\u60a8\u662f\u5426\u4f1a\u5b9a\u671f\u5bf9\u5e94\u7528\u7a0b\u5e8f\u8fdb\u884c\u57fa\u51c6\u6d4b\u8bd5\u5e76\u5bfb\u627e\u4efb\u4f55\u56de\u5f52\uff1f\u5982\u679c\u662f\u8fd9\u6837\uff0c\u6700\u5c0f API \u53ef\u80fd\u662f\u66f4\u597d\u7684\u9009\u62e9\uff0c\u56e0\u4e3a\u5b83\u4eec\u901a\u5e38\u6bd4 Web API \u63a7\u5236\u5668\u6027\u80fd\u66f4\u9ad8\u3002<\/p>\n<p>The MVC framework that web API controllers use relies on a lot of conventions and reflection for discovering your controllers and a complex filter pipeline. These are obviously highly optimized, but if you\u2019re writing an application where you need to squeeze out every little bit of throughput, minimal APIs will likely help get you there more easily. For most applications, the overhead of the MVC framework will be negligible when compared with any database or network access in your app, so this is worth worrying about only for performance-sensitive apps.<br \/>\nWeb API \u63a7\u5236\u5668\u4f7f\u7528\u7684 MVC \u6846\u67b6\u4f9d\u8d56\u4e8e\u8bb8\u591a\u7ea6\u5b9a\u548c\u53cd\u5c04\u6765\u53d1\u73b0\u63a7\u5236\u5668\u548c\u590d\u6742\u7684\u7b5b\u9009\u5668\u7ba1\u9053\u3002\u8fd9\u4e9b\u663e\u7136\u662f\u9ad8\u5ea6\u4f18\u5316\u7684\uff0c\u4f46\u5982\u679c\u60a8\u6b63\u5728\u7f16\u5199\u4e00\u4e2a\u9700\u8981\u6324\u51fa\u6bcf\u4e00\u70b9\u541e\u5410\u91cf\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u90a3\u4e48\u6700\u5c11\u7684 API \u53ef\u80fd\u4f1a\u5e2e\u52a9\u60a8\u66f4\u8f7b\u677e\u5730\u5b9e\u73b0\u76ee\u6807\u3002\u5bf9\u4e8e\u5927\u591a\u6570\u5e94\u7528\u7a0b\u5e8f\uff0c\u4e0e\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u4efb\u4f55\u6570\u636e\u5e93\u6216\u7f51\u7edc\u8bbf\u95ee\u76f8\u6bd4\uff0cMVC \u6846\u67b6\u7684\u5f00\u9500\u53ef\u4ee5\u5ffd\u7565\u4e0d\u8ba1\uff0c\u56e0\u6b64\uff0c\u4ec5\u5bf9\u4e8e\u6027\u80fd\u654f\u611f\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u624d\u503c\u5f97\u62c5\u5fc3\u3002<\/p>\n<p>Question 3 focuses on filtering. You learned about filtering with minimal APIs in chapter 5: filters allow you to attach a processing pipeline to your minimal API endpoints and can be used to do things like automatic validation. Web API controllers (as well as MVC controllers and Razor Pages) also have a filter pipeline, but it\u2019s much more complex than the simple pipeline used by minimal APIs, as you\u2019ll see in chapters 21 and 22.<br \/>\n\u95ee\u9898 3 \u4fa7\u91cd\u4e8e\u7b5b\u9009\u3002\u60a8\u5728\u7b2c 5 \u7ae0\u4e2d\u5b66\u4e60\u4e86\u5982\u4f55\u4f7f\u7528\u6700\u5c11\u7684 API \u8fdb\u884c\u8fc7\u6ee4\uff1a\u8fc7\u6ee4\u5668\u5141\u8bb8\u60a8\u5c06\u5904\u7406\u7ba1\u9053\u9644\u52a0\u5230\u6700\u5c0f\u7684 API \u7aef\u70b9\uff0c\u5e76\u53ef\u7528\u4e8e\u6267\u884c\u81ea\u52a8\u9a8c\u8bc1\u7b49\u4f5c\u3002Web API \u63a7\u5236\u5668\uff08\u4ee5\u53ca MVC \u63a7\u5236\u5668\u548c Razor Pages\uff09\u4e5f\u6709\u4e00\u4e2a\u7b5b\u9009\u5668\u7ba1\u9053\uff0c\u4f46\u5b83\u6bd4\u6700\u5c0f API \u4f7f\u7528\u7684\u7b80\u5355\u7ba1\u9053\u8981\u590d\u6742\u5f97\u591a\uff0c\u5982\u7b2c 21 \u7ae0\u548c\u7b2c 22 \u7ae0\u6240\u793a\u3002<\/p>\n<p>In most cases the filtering provided by minimal APIs will be perfectly adequate for your needs. The main cases where minimal API filtering will fall down will be when you already have an application that uses web API controllers and want to reuse some complex filters. In these cases, there may be no way to translate your existing web API filters to minimal API filters. If the filtering is important, then you may need to stick with web API controllers.<br \/>\n\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u6700\u5c0f API \u63d0\u4f9b\u7684\u8fc7\u6ee4\u5c06\u5b8c\u5168\u6ee1\u8db3\u60a8\u7684\u9700\u6c42\u3002\u6700\u5c0f API \u8fc7\u6ee4\u5931\u8d25\u7684\u4e3b\u8981\u60c5\u51b5\u662f\uff0c\u60a8\u5df2\u7ecf\u6709\u4e00\u4e2a\u4f7f\u7528 Web API \u63a7\u5236\u5668\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u4e14\u60f3\u8981\u91cd\u7528\u4e00\u4e9b\u590d\u6742\u7684\u8fc7\u6ee4\u5668\u3002\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u53ef\u80fd\u65e0\u6cd5\u5c06\u73b0\u6709\u7684 Web API \u7b5b\u9009\u5668\u8f6c\u6362\u4e3a\u6700\u5c0f\u7684 API \u7b5b\u9009\u5668\u3002\u5982\u679c\u7b5b\u9009\u5f88\u91cd\u8981\uff0c\u90a3\u4e48\u4f60\u53ef\u80fd\u9700\u8981\u575a\u6301\u4f7f\u7528 Web API \u63a7\u5236\u5668\u3002<\/p>\n<p>This leads to question 4: are you building a new application or working on an existing application? If this is a new application, I would be strongly in favor of using minimal APIs. Minimal APIs are conceptually simpler than web API controllers, are faster because of this, and are receiving a lot of improvements from the ASP.NET Core team. If there\u2019s no other compelling reason to choose web API controllers in your new project, I suggest defaulting to minimal APIs.<br \/>\n\u8fd9\u5c31\u5f15\u51fa\u4e86\u95ee\u9898 4\uff1a\u60a8\u662f\u6784\u5efa\u65b0\u5e94\u7528\u7a0b\u5e8f\u8fd8\u662f\u6b63\u5728\u5904\u7406\u73b0\u6709\u5e94\u7528\u7a0b\u5e8f\uff1f\u5982\u679c\u8fd9\u662f\u4e00\u4e2a\u65b0\u5e94\u7528\u7a0b\u5e8f\uff0c\u6211\u5f3a\u70c8\u8d5e\u6210\u4f7f\u7528\u6700\u5c11\u7684 API\u3002\u6700\u5c0f API \u5728\u6982\u5ff5\u4e0a\u6bd4 Web API \u63a7\u5236\u5668\u66f4\u7b80\u5355\uff0c\u56e0\u6b64\u901f\u5ea6\u66f4\u5feb\uff0c\u5e76\u4e14\u4ece ASP.NET Core \u56e2\u961f\u83b7\u5f97\u4e86\u5f88\u591a\u6539\u8fdb\u3002\u5982\u679c\u6ca1\u6709\u5176\u4ed6\u4ee4\u4eba\u4fe1\u670d\u7684\u7406\u7531\u5728\u4f60\u7684\u65b0\u9879\u76ee\u4e2d\u9009\u62e9 Web API \u63a7\u5236\u5668\uff0c\u6211\u5efa\u8bae\u9ed8\u8ba4\u4f7f\u7528\u6700\u5c0f API\u3002<\/p>\n<p>On the other hand, if you have an existing web API controller application, I would be strongly inclined to stick with web API controllers. While it\u2019s perfectly possible to mix minimal APIs and web API controllers in the same application, I would favor consistency over using the new hotness.<br \/>\n\u53e6\u4e00\u65b9\u9762\uff0c\u5982\u679c\u4f60\u6709\u4e00\u4e2a\u73b0\u6709\u7684 Web API \u63a7\u5236\u5668\u5e94\u7528\u7a0b\u5e8f\uff0c\u6211\u5f3a\u70c8\u503e\u5411\u4e8e\u575a\u6301\u4f7f\u7528 Web API \u63a7\u5236\u5668\u3002\u867d\u7136\u5b8c\u5168\u53ef\u4ee5\u5728\u540c\u4e00\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6df7\u5408\u4f7f\u7528\u6700\u5c11\u7684 API \u548c Web API \u63a7\u5236\u5668\uff0c\u4f46\u6211\u66f4\u559c\u6b22\u4e00\u81f4\u6027\uff0c\u800c\u4e0d\u662f\u4f7f\u7528\u65b0\u7684\u70ed\u5ea6\u3002<\/p>\n<p>Question 5 considers how familiar you already are with web API controllers. If you\u2019re coming from legacy ASP.NET or have already used web API controllers in ASP.NET Core and need to be productive quickly, you might decide to stick with web API controllers.<br \/>\n\u95ee\u9898 5 \u8003\u8651\u60a8\u5bf9 Web API \u63a7\u5236\u5668\u7684\u719f\u6089\u7a0b\u5ea6\u3002\u5982\u679c\u60a8\u6765\u81ea\u4f20\u7edf ASP.NET \u6216\u5df2\u7ecf\u5728 ASP.NET Core \u4e2d\u4f7f\u7528\u8fc7 Web API \u63a7\u5236\u5668\uff0c\u5e76\u4e14\u9700\u8981\u5feb\u901f\u63d0\u9ad8\u5de5\u4f5c\u6548\u7387\uff0c\u5219\u53ef\u4ee5\u51b3\u5b9a\u7ee7\u7eed\u4f7f\u7528 Web API \u63a7\u5236\u5668\u3002<\/p>\n<p>I consider this one of the weaker arguments, as minimal APIs are conceptually simpler than web API controllers; if you already know web API controllers, you will likely pick up minimal APIs easily. That said, the differences in the model binding approaches can be a little confusing, and you may decide it\u2019s not worth the investment or frustration if things don\u2019t work as you expect.<br \/>\n\u6211\u8ba4\u4e3a\u8fd9\u662f\u8f83\u5f31\u7684\u8bba\u70b9\u4e4b\u4e00\uff0c\u56e0\u4e3a\u6700\u5c0f\u7684 API \u5728\u6982\u5ff5\u4e0a\u6bd4 Web API \u63a7\u5236\u5668\u7b80\u5355;\u5982\u679c\u60a8\u5df2\u7ecf\u4e86\u89e3 Web API \u63a7\u5236\u5668\uff0c\u5219\u53ef\u80fd\u4f1a\u8f7b\u677e\u638c\u63e1\u6700\u5c11\u7684 API\u3002\u4e5f\u5c31\u662f\u8bf4\uff0c\u6a21\u578b\u7ed1\u5b9a\u65b9\u6cd5\u7684\u5dee\u5f02\u53ef\u80fd\u6709\u70b9\u4ee4\u4eba\u56f0\u60d1\uff0c\u5982\u679c\u4e8b\u60c5\u6ca1\u6709\u6309\u9884\u671f\u8fdb\u884c\uff0c\u60a8\u53ef\u80fd\u4f1a\u8ba4\u4e3a\u4e0d\u503c\u5f97\u6295\u8d44\u6216\u611f\u5230\u6cae\u4e27\u3002<\/p>\n<p>The final question comes down entirely to taste and preference: do you like minimal APIs? web API controllers heavily follow the \u201cconvention over configuration\u201d paradigm (though not to the extent of MVC controllers and Razor Pages). By contrast, you must be far more explicit with minimal APIs. Minimal APIs also don\u2019t enforce any particular grouping, unlike web API controllers, which all follow the \u201caction methods in a controller class\u201d pattern.<br \/>\n\u6700\u540e\u4e00\u4e2a\u95ee\u9898\u5b8c\u5168\u5f52\u7ed3\u4e3a\u54c1\u5473\u548c\u504f\u597d\uff1a\u60a8\u559c\u6b22\u6700\u5c11\u7684 API \u5417\uff1fWeb API \u63a7\u5236\u5668\u5728\u5f88\u5927\u7a0b\u5ea6\u4e0a\u9075\u5faa\u201c\u7ea6\u5b9a\u4f18\u4e8e\u914d\u7f6e\u201d\u8303\u4f8b\uff08\u5c3d\u7ba1\u6ca1\u6709\u8fbe\u5230 MVC \u63a7\u5236\u5668\u548c Razor Pages \u7684\u8303\u56f4\uff09\u3002\u76f8\u6bd4\u4e4b\u4e0b\uff0c\u60a8\u5fc5\u987b\u4f7f\u7528\u6700\u5c11\u7684 API \u66f4\u52a0\u660e\u786e\u3002\u4e0e Web API \u63a7\u5236\u5668\u4e0d\u540c\uff0c\u6700\u5c0f API \u4e5f\u4e0d\u5f3a\u5236\u6267\u884c\u4efb\u4f55\u7279\u5b9a\u7684\u5206\u7ec4\uff0c\u5b83\u4eec\u90fd\u9075\u5faa\u201c\u63a7\u5236\u5668\u7c7b\u4e2d\u7684\u4f5c\u65b9\u6cd5\u201d\u6a21\u5f0f\u3002<\/p>\n<p>Different people prefer different approaches. Web API controllers mean less manual wiring up of components, but this necessarily means more magic and more rigidity around how you structure your applications.<br \/>\n\u4e0d\u540c\u7684\u4eba\u559c\u6b22\u4e0d\u540c\u7684\u65b9\u6cd5\u3002Web API \u63a7\u5236\u5668\u610f\u5473\u7740\u66f4\u5c11\u7684\u7ec4\u4ef6\u624b\u52a8\u8fde\u63a5\uff0c\u4f46\u8fd9\u5fc5\u7136\u610f\u5473\u7740\u5728\u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u7684\u65b9\u5f0f\u4e0a\u66f4\u795e\u5947\u3001\u66f4\u4e25\u683c\u3002<\/p>\n<p>By contrast, minimal API endpoints must be explicitly added to the WebApplication instance, but this also means you have more flexibility around how to group your endpoints. You can put all your endpoints in Program.cs, create natural groupings for them in separate classes, or create a file per endpoint or any pattern you choose.<br \/>\n\u76f8\u6bd4\u4e4b\u4e0b\uff0c\u5fc5\u987b\u5c06\u6700\u5c11\u7684 API \u7aef\u70b9\u663e\u5f0f\u6dfb\u52a0\u5230 WebApplication \u5b9e\u4f8b\u4e2d\uff0c\u4f46\u8fd9\u4e5f\u610f\u5473\u7740\u60a8\u5728\u5982\u4f55\u5bf9\u7aef\u70b9\u8fdb\u884c\u5206\u7ec4\u65b9\u9762\u5177\u6709\u66f4\u5927\u7684\u7075\u6d3b\u6027\u3002\u60a8\u53ef\u4ee5\u5c06\u6240\u6709\u7ec8\u7aef\u8282\u70b9\u653e\u5728Program.cs\u4e2d\uff0c\u5728\u5355\u72ec\u7684\u7c7b\u4e2d\u4e3a\u5b83\u4eec\u521b\u5efa\u81ea\u7136\u5206\u7ec4\uff0c\u6216\u8005\u4e3a\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u6216\u60a8\u9009\u62e9\u7684\u4efb\u4f55\u6a21\u5f0f\u521b\u5efa\u4e00\u4e2a\u6587\u4ef6\u3002<\/p>\n<p><b>Tip<\/b> You can also more easily layer on helper frameworks to minimal APIs, such as Carter (<a href=\"https:\/\/github.com\/CarterCommunity\/Carter\">https:\/\/github.com\/CarterCommunity\/Carter<\/a>), which can provide some structure and support functionality if you want it.<br \/>\n\u63d0\u793a:\u60a8\u8fd8\u53ef\u4ee5\u66f4\u8f7b\u677e\u5730\u5c06\u5e2e\u52a9\u7a0b\u5e8f\u6846\u67b6\u5206\u5c42\u5230\u6700\u5c0f\u7684 API\uff0c\u4f8b\u5982 Carter \uff08<a href=\"https:\/\/github.com\/CarterCommunity\/Carter\uff09\uff0c\u5982\u679c\u9700\u8981\uff0c\u5b83\u53ef\u4ee5\u63d0\u4f9b\u4e00\u4e9b\u7ed3\u6784\u548c\u652f\u6301\u529f\u80fd\">https:\/\/github.com\/CarterCommunity\/Carter\uff09\uff0c\u5982\u679c\u9700\u8981\uff0c\u5b83\u53ef\u4ee5\u63d0\u4f9b\u4e00\u4e9b\u7ed3\u6784\u548c\u652f\u6301\u529f\u80fd<\/a>\u3002<\/p>\n<p>Overall, the choice is up to you whether web API controllers or minimal APIs are better for your application. Table 20.1 summarizes the questions and where you should favor one approach over the other, but the final choice is up to you!<br \/>\n\u603b\u7684\u6765\u8bf4\uff0cWeb API \u63a7\u5236\u5668\u8fd8\u662f\u6700\u5c0f\u7684 API \u66f4\u9002\u5408\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u7531\u60a8\u51b3\u5b9a\u3002\u8868 20.1 \u603b\u7ed3\u4e86\u8fd9\u4e9b\u95ee\u9898\u4ee5\u53ca\u60a8\u5e94\u8be5\u5728\u54ea\u4e9b\u65b9\u9762\u66f4\u559c\u6b22\u4e00\u79cd\u65b9\u6cd5\uff0c\u4f46\u6700\u7ec8\u9009\u62e9\u53d6\u51b3\u4e8e\u60a8\uff01<\/p>\n<p>Table 20.1 Choosing between minimal APIs with web API controllers<br \/>\n\u8868 20.1 \u5728\u6700\u5c0f API \u548c Web API \u63a7\u5236\u5668\u4e4b\u95f4\u8fdb\u884c\u9009\u62e9<\/p>\n<table>\n<thead>\n<tr>\n<th>Question<\/th>\n<th>Minimal APIs<\/th>\n<th>Web API controllers<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1. Do you need conneg?<\/td>\n<td>Can\u2019t use conneg out of the box<\/td>\n<td>Built-in and extensible<\/td>\n<\/tr>\n<tr>\n<td>2. How critical is performance?<\/td>\n<td>More performant than web API controllers<\/td>\n<td>Less performant than minimal APIs<\/td>\n<\/tr>\n<tr>\n<td>3. Complex filtering?<\/td>\n<td>Have a simple, extensible filter pipeline<\/td>\n<td>Have a complex, nonlinear, filter pipeline<\/td>\n<\/tr>\n<tr>\n<td>4. Is this a new project?<\/td>\n<td>Minimal APIs are getting many new features and are a focus of the ASP.NET Core team<\/td>\n<td>The MVC framework is receiving small new features, but is less of a focus.<\/td>\n<\/tr>\n<tr>\n<td>5. Do you have experience with web API controllers?<\/td>\n<td>Minimal APIs share many of the same concepts, but have subtle differences in model binding<\/td>\n<td>Web API controllers may be familiar to users of legacy ASP.NET or older ASP.NET Core versions<\/td>\n<\/tr>\n<tr>\n<td>6. Do you prefer convention over configuration?<\/td>\n<td>Requires a lot of explicit configuration<\/td>\n<td>Convention- and discovery-based, which can appear more magic when you\u2019re unfamiliar<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>That brings us to the end of this chapter on web APIs. In the next chapter we\u2019ll look at one of more advanced topics of MVC and Razor Pages: the filter pipeline and how you can use it to reduce duplication in your code. The good news is that it\u2019s similar to minimal API filters in principle. The bad news is that it\u2019s far more complicated!<br \/>\n\u8fd9\u8ba9\u6211\u4eec\u7ed3\u675f\u4e86\u672c\u7ae0\u5173\u4e8e Web API \u7684\u5185\u5bb9\u3002\u5728\u4e0b\u4e00\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd MVC \u548c Razor Pages \u7684\u66f4\u9ad8\u7ea7\u4e3b\u9898\u4e4b\u4e00\uff1a\u7b5b\u9009\u5668\u7ba1\u9053\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u5b83\u6765\u51cf\u5c11\u4ee3\u7801\u4e2d\u7684\u91cd\u590d\u3002\u597d\u6d88\u606f\u662f\uff0c\u5b83\u5728\u539f\u5219\u4e0a\u7c7b\u4f3c\u4e8e\u6700\u5c0f API \u8fc7\u6ee4\u5668\u3002\u574f\u6d88\u606f\u662f\u5b83\u8981\u590d\u6742\u5f97\u591a\uff01<\/p>\n<h2>20.7 Summary<\/h2>\n<p>20.7 \u603b\u7ed3<\/p>\n<p>Web API action methods can return data directly or can use <code>ActionResult&lt;T&gt;<\/code> to generate an arbitrary response. If you return more than one type of result from an action method, the method signature must return <code>ActionResult&lt;T&gt;<\/code>.<br \/>\nWeb API\u4f5c\u65b9\u6cd5\u53ef\u4ee5\u76f4\u63a5\u8fd4\u56de\u6570\u636e\uff0c\u4e5f\u53ef\u4ee5\u7528\u4e8e <code>ActionResult&lt;T&gt;<\/code>\u751f\u6210\u4efb\u610f\u54cd\u5e94\u3002\u5982\u679c\u4ece\u4f5c\u65b9\u6cd5\u8fd4\u56de\u591a\u79cd\u7c7b\u578b\u7684\u7ed3\u679c\uff0c\u5219\u65b9\u6cd5\u7b7e\u540d\u5fc5\u987b\u8fd4\u56de <code>ActionResult&lt;T&gt;<\/code>\u3002<\/p>\n<p>The data returned by a web API action is sometimes called an API model. It contains the data that will be serialized and send back to the client. This differs from view models and PageModels, which contain both data and metadata about how to generate the response.<br \/>\nWeb API\u4f5c\u8fd4\u56de\u7684\u6570\u636e\u6709\u65f6\u79f0\u4e3a API \u6a21\u578b\u3002\u5b83\u5305\u542b\u5c06\u88ab\u5e8f\u5217\u5316\u5e76\u53d1\u9001\u56de\u5ba2\u6237\u7aef\u7684\u6570\u636e\u3002\u8fd9\u4e0e\u89c6\u56fe\u6a21\u578b\u548c PageModel \u4e0d\u540c\uff0c\u540e\u8005\u5305\u542b\u6709\u5173\u5982\u4f55\u751f\u6210\u54cd\u5e94\u7684\u6570\u636e\u548c\u5143\u6570\u636e\u3002<\/p>\n<p>Web APIs are associated with route templates by applying RouteAttributes to your action methods. These give you complete control over the URLs that make up your application\u2019s API.<br \/>\n\u901a\u8fc7\u5c06 RouteAttributes \u5e94\u7528\u4e8e\u4f5c\u65b9\u6cd5\uff0cWeb API \u4e0e\u8def\u7531\u6a21\u677f\u76f8\u5173\u8054\u3002\u8fd9\u4e9b URL \u4f7f\u60a8\u53ef\u4ee5\u5b8c\u5168\u63a7\u5236\u6784\u6210\u5e94\u7528\u7a0b\u5e8f API \u7684 URL\u3002<\/p>\n<p>Route attributes applied to a controller combine with the attributes on action methods to form the final template. These are also combined with attributes on inherited base classes. You can use inherited attributes to reduce the amount of duplication in the attributes, such as where you\u2019re using a common prefix on your routes.<br \/>\n\u5e94\u7528\u4e8e\u63a7\u5236\u5668\u7684\u8def\u7531\u5c5e\u6027\u4e0e\u4f5c\u65b9\u6cd5\u4e0a\u7684\u5c5e\u6027\u76f8\u7ed3\u5408\uff0c\u5f62\u6210\u6700\u7ec8\u6a21\u677f\u3002\u8fd9\u4e9b\u8fd8\u4e0e\u7ee7\u627f\u7684\u57fa\u7c7b\u4e0a\u7684\u5c5e\u6027\u76f8\u7ed3\u5408\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u7ee7\u627f\u7684\u5c5e\u6027\u6765\u51cf\u5c11\u5c5e\u6027\u4e2d\u7684\u91cd\u590d\u6570\u91cf\uff0c\u4f8b\u5982\u5728\u8def\u7531\u4e0a\u4f7f\u7528\u901a\u7528\u524d\u7f00\u7684\u4f4d\u7f6e\u3002<\/p>\n<p>By default, the controller and action name have no bearing on the URLs or route templates when you use attribute routing. However, you can use the &quot;[controller]&quot; and &quot;[action]&quot; tokens in your route templates to reduce repetition. They\u2019ll be replaced with the current controller and action name.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u5728\u4f7f\u7528\u5c5e\u6027\u8def\u7531\u65f6\uff0c\u63a7\u5236\u5668\u548c\u4f5c\u540d\u79f0\u4e0e URL \u6216\u8def\u7531\u6a21\u677f\u65e0\u5173\u3002\u4f46\u662f\uff0c\u60a8\u53ef\u4ee5\u5728\u8def\u7531\u6a21\u677f\u4e2d\u4f7f\u7528 \u201c[controller]\u201d \u548c \u201c[action]\u201d \u4ee4\u724c\u6765\u51cf\u5c11\u91cd\u590d\u3002\u5b83\u4eec\u5c06\u66ff\u6362\u4e3a\u5f53\u524d\u63a7\u5236\u5668\u548c\u4f5c\u540d\u79f0\u3002<\/p>\n<p>The [HttpPost] and [HttpGet] attributes allow you to choose between actions based on the request\u2019s HTTP verb when two actions correspond to the same URL. This is a common pattern in RESTful applications.<br \/>\n[HttpPost] \u548c [HttpGet] \u5c5e\u6027\u5141\u8bb8\u60a8\u5728\u4e24\u4e2a\u4f5c\u5bf9\u5e94\u4e8e\u540c\u4e00 URL \u65f6\u6839\u636e\u8bf7\u6c42\u7684 HTTP \u8c13\u8bcd\u5728\u4f5c\u4e4b\u95f4\u8fdb\u884c\u9009\u62e9\u3002\u8fd9\u662f RESTful \u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u5e38\u89c1\u6a21\u5f0f\u3002<\/p>\n<p>The [ApiController] attribute applies several common conventions to your controllers. Controllers decorated with the attribute automatically bind to a request\u2019s body instead of using form values, automatically generate a 400 Bad Request response for invalid requests, and return ProblemDetails objects for status code errors. This can dramatically reduce the amount of boilerplate code you must write.<br \/>\n[ApiController] \u5c5e\u6027\u5c06\u51e0\u4e2a\u5e38\u89c1\u7ea6\u5b9a\u5e94\u7528\u4e8e\u63a7\u5236\u5668\u3002\u4f7f\u7528 \u8be5\u5c5e\u6027\u4fee\u9970\u7684\u63a7\u5236\u5668\u4f1a\u81ea\u52a8\u7ed1\u5b9a\u5230\u8bf7\u6c42\u6b63\u6587\uff0c\u800c\u4e0d\u662f\u4f7f\u7528\u8868\u5355\u503c\uff0c\u4e3a\u65e0\u6548\u8bf7\u6c42\u81ea\u52a8\u751f\u6210 400 Bad Request \u54cd\u5e94\uff0c\u5e76\u4e3a\u72b6\u6001\u4ee3\u7801\u9519\u8bef\u8fd4\u56de ProblemDetails \u5bf9\u8c61\u3002\u8fd9\u53ef\u4ee5\u663e\u8457\u51cf\u5c11\u60a8\u5fc5\u987b\u7f16\u5199\u7684\u6837\u677f\u4ee3\u7801\u91cf\u3002<\/p>\n<p>You can control which of the conventions to apply by using the ConfigureApiBehaviorOptions() method and providing a configuration lambda. This is useful if you need to fit your API to an existing specification, for example.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528 ConfigureApiBehaviorOptions\uff08\uff09 \u65b9\u6cd5\u5e76\u63d0\u4f9b\u914d\u7f6e lambda \u6765\u63a7\u5236\u8981\u5e94\u7528\u7684\u7ea6\u5b9a\u3002\u4f8b\u5982\uff0c\u5982\u679c\u60a8\u9700\u8981\u4f7f API \u9002\u5e94\u73b0\u6709\u89c4\u8303\uff0c\u8fd9\u5c06\u975e\u5e38\u6709\u7528\u3002<\/p>\n<p>By default, ASP.NET Core formats the API model returned from a web API controller as JSON. In contrast to legacy ASP.NET, JSON data is serialized using camelCase rather than PascalCase. You should consider this change if you get errors or missing values when using data from your API.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cASP.NET Core \u5c06\u4ece Web API \u63a7\u5236\u5668\u8fd4\u56de\u7684 API \u6a21\u578b\u683c\u5f0f\u5316\u4e3a JSON\u3002\u4e0e\u4f20\u7edf ASP.NET \u76f8\u6bd4\uff0cJSON \u6570\u636e\u4f7f\u7528 camelCase \u800c\u4e0d\u662f PascalCase \u8fdb\u884c\u5e8f\u5217\u5316\u3002\u5982\u679c\u60a8\u5728\u4f7f\u7528 API \u4e2d\u7684\u6570\u636e\u65f6\u51fa\u73b0\u9519\u8bef\u6216\u7f3a\u5931\u503c\uff0c\u5219\u5e94\u8003\u8651\u6b64\u66f4\u6539\u3002<\/p>\n<p>ASP.NET Core 3.0 onwards uses System.Text.Json, which is a strict, high performance library for JSON serialization and deserialization. You can replace this serializer with the common Newtonsoft.Json formatter by calling AddNewtonsoftJson() on the return value from services.AddControllers().<br \/>\nASP.NET Core 3.0 \u53ca\u66f4\u9ad8\u7248\u672c\u4f7f\u7528 System.Text.Json\uff0c\u8fd9\u662f\u4e00\u4e2a\u4e25\u683c\u7684\u9ad8\u6027\u80fd\u5e93\uff0c\u7528\u4e8e JSON \u5e8f\u5217\u5316\u548c\u53cd\u5e8f\u5217\u5316\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5bf9\u670d\u52a1\u7684\u8fd4\u56de\u503c\u8c03\u7528 AddNewtonsoftJson\uff08\uff09 \u6765\u5c06\u6b64\u5e8f\u5217\u5316\u7a0b\u5e8f\u66ff\u6362\u4e3a\u901a\u7528\u7684 Newtonsoft.Json \u683c\u5f0f\u5316\u7a0b\u5e8f\u3002AddControllers\uff08\uff09 \u7684 Controller\u3002<\/p>\n<p>Content negotiation occurs when the client specifies the type of data it can handle and the server chooses a return format based on this. It allows multiple clients to call your API and receive data in a format they can understand.<br \/>\n\u5f53\u5ba2\u6237\u7aef\u6307\u5b9a\u5b83\u53ef\u4ee5\u5904\u7406\u7684\u6570\u636e\u7c7b\u578b\uff0c\u5e76\u4e14\u670d\u52a1\u5668\u6839\u636e\u6b64\u9009\u62e9\u8fd4\u56de\u683c\u5f0f\u65f6\uff0c\u5c31\u4f1a\u53d1\u751f\u5185\u5bb9\u534f\u5546\u3002\u5b83\u5141\u8bb8\u591a\u4e2a\u5ba2\u6237\u7aef\u8c03\u7528\u60a8\u7684 API \u5e76\u4ee5\u4ed6\u4eec\u53ef\u4ee5\u7406\u89e3\u7684\u683c\u5f0f\u63a5\u6536\u6570\u636e\u3002<\/p>\n<p>By default, ASP.NET Core can return text\/plain, text\/html, and application\/json, but you can add formatters if you need to support other formats.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cASP.NET Core \u53ef\u4ee5\u8fd4\u56de text\/plain\u3001text\/html \u548c application\/json\uff0c\u4f46\u5982\u679c\u60a8\u9700\u8981\u652f\u6301\u5176\u4ed6\u683c\u5f0f\uff0c\u5219\u53ef\u4ee5\u6dfb\u52a0\u683c\u5f0f\u5316\u7a0b\u5e8f\u3002<\/p>\n<p>You can add XML formatters by calling AddXmlSerializerFormatters() on the return value from services.AddControllers() in your Startup class. These can format the response as XML, as well as receive XML in a request body.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5bf9\u670d\u52a1\u7684\u8fd4\u56de\u503c\u8c03\u7528 AddXmlSerializerFormatters\uff08\uff09 \u6765\u6dfb\u52a0 XML \u683c\u5f0f\u5316\u7a0b\u5e8f\u3002AddControllers\uff08\uff09 \u7684 Startup\u3002\u8fd9\u4e9b\u9009\u9879\u53ef\u4ee5\u5c06\u54cd\u5e94\u683c\u5f0f\u5316\u4e3a XML\uff0c\u5e76\u5728\u8bf7\u6c42\u6b63\u6587\u4e2d\u63a5\u6536 XML\u3002<\/p>\n<p>Content negotiation isn\u2019t used when the Accept header contains <em>\/<\/em>, such as in most browsers. Instead, your application uses the default formatter, JSON. You can disable this option by setting the RespectBrowserAcceptHeader option to true when adding your controller services in Program.cs.<br \/>\n\u5f53 Accept \u6807\u5934\u5305\u542b \/ \u65f6\uff0c\u4e0d\u4f1a\u4f7f\u7528\u5185\u5bb9\u534f\u5546\uff0c\u4f8b\u5982\u5728\u5927\u591a\u6570\u6d4f\u89c8\u5668\u4e2d\u3002\u76f8\u53cd\uff0c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528\u9ed8\u8ba4\u683c\u5f0f\u5316\u7a0b\u5e8f JSON\u3002\u5728 Program.cs \u4e2d\u6dfb\u52a0\u63a7\u5236\u5668\u670d\u52a1\u65f6\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06 RespectBrowserAcceptHeader \u9009\u9879\u8bbe\u7f6e\u4e3a true \u6765\u7981\u7528\u6b64\u9009\u9879\u3002<\/p>\n<p>You can mix web API Controllers and minimal API endpoints in the same application, but you may find it easier to use one or the other.<br \/>\n\u60a8\u53ef\u4ee5\u5728\u540c\u4e00\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6df7\u5408\u4f7f\u7528 Web API \u63a7\u5236\u5668\u548c\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\uff0c\u4f46\u60a8\u53ef\u80fd\u4f1a\u53d1\u73b0\u4f7f\u7528\u5176\u4e2d\u4e00\u79cd\u66f4\u5bb9\u6613\u3002<\/p>\n<p>Choose web API controllers when you need content negotiation, when you have complex filtering requirements, when you have experience with web controllers, or when you prefer convention over configuration for your apps.<br \/>\n\u5f53\u60a8\u9700\u8981\u5185\u5bb9\u534f\u5546\u3001\u6709\u590d\u6742\u7684\u7b5b\u9009\u8981\u6c42\u3001\u5177\u6709 Web \u63a7\u5236\u5668\u4f7f\u7528\u7ecf\u9a8c\u65f6\uff0c\u6216\u8005\u5f53\u60a8\u66f4\u559c\u6b22\u5e94\u7528\u7a0b\u5e8f\u7684\u7ea6\u5b9a\u800c\u4e0d\u662f\u914d\u7f6e\u65f6\uff0c\u8bf7\u9009\u62e9 Web API \u63a7\u5236\u5668\u3002<\/p>\n<p>Choose minimal API endpoints when performance is critical, when you prefer explicit configuration over automatic conventions, or when you\u2019re starting a new app.<br \/>\n\u5f53\u6027\u80fd\u81f3\u5173\u91cd\u8981\u65f6\uff0c\u5f53\u60a8\u66f4\u559c\u6b22\u663e\u5f0f\u914d\u7f6e\u800c\u4e0d\u662f\u81ea\u52a8\u7ea6\u5b9a\u65f6\uff0c\u6216\u8005\u5f53\u60a8\u542f\u52a8\u65b0\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u8bf7\u9009\u62e9\u6700\u5c11\u7684 API \u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>20 Creating an HTTP API using web API controllers 20 \u4f7f\u7528 Web API \u63a7\u5236\u5668\u521b\u5efa HTTP API This chapter covers \u672c\u7ae0\u6db5\u76d6 \u2022 Creating a web API controller to return JavaScript Object Notation (JSON) to clients \u521b\u5efa Web API \u63a7\u5236\u5668\u4ee5\u5c06 JavaScript \u5bf9\u8c61\u8868\u793a\u6cd5 \uff08JSON\uff09 \u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef \u2022 Using attribute routing to customize your URLs \u4f7f\u7528\u5c5e\u6027\u8def\u7531\u81ea\u5b9a\u4e49 URL \u2022 Generating a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-611","post","type-post","status-publish","format-standard","hentry","category-csharp"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/611","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=611"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/611\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=611"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=611"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=611"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}