{"id":593,"date":"2025-04-05T11:37:54","date_gmt":"2025-04-05T03:37:54","guid":{"rendered":"https:\/\/www.hyy.net\/?p=593"},"modified":"2025-04-05T11:37:54","modified_gmt":"2025-04-05T03:37:54","slug":"asp-net-core-in-action-11-documenting-apis-with-openapi","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=593","title":{"rendered":"ASP.NET Core in Action 11 Documenting APIs with OpenAPI"},"content":{"rendered":"<p>11 Documenting APIs with OpenAPI<br \/>\n11  \u4f7f\u7528 OpenAPI \u8bb0\u5f55 API<\/p>\n<h2>This chapter covers<\/h2>\n<h2>\u672c\u7ae0\u6db5\u76d6<\/h2>\n<ul>\n<li>Understanding OpenAPI and seeing why it\u2019s useful<br \/>\n\u4e86\u89e3 OpenAPI \u5e76\u4e86\u89e3\u5b83\u4e3a\u4f55\u6709\u7528<\/li>\n<li>Adding an OpenAPI description to your app<br \/>\n\u5c06 OpenAPI \u63cf\u8ff0\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f<\/li>\n<li>Improving your OpenAPI descriptions by adding metadata to endpoints<br \/>\n\u901a\u8fc7\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u5143\u6570\u636e\u6765\u6539\u8fdb OpenAPI \u63cf\u8ff0<\/li>\n<li>Generating a C# client from your OpenAPI description<br \/>\n\u4ece OpenAPI \u63cf\u8ff0\u751f\u6210 C# \u5ba2\u6237\u7aef<\/li>\n<\/ul>\n<p>In this chapter I introduce the OpenAPI specification for describing RESTful APIs, demonstrate how to use OpenAPI to describe a minimal API application, and discuss some of the reasons you might want to do so.<\/p>\n<p>\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u7528\u4e8e\u63cf\u8ff0 RESTful API \u7684 OpenAPI \u89c4\u8303\uff0c\u6f14\u793a\u5982\u4f55\u4f7f\u7528 OpenAPI \u6765\u63cf\u8ff0\u6700\u5c0f\u7684 API \u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u8ba8\u8bba\u60a8\u53ef\u80fd\u5e0c\u671b\u8fd9\u6837\u505a\u7684\u4e00\u4e9b\u539f\u56e0\u3002<\/p>\n<p>In section 11.1 you\u2019ll learn about the OpenAPI specification itself and where it fits in to an ASP.NET Core application. You\u2019ll learn about the libraries you can use to enable OpenAPI documentation generation in your app and how to expose the document using middleware.<\/p>\n<p>\u5728\u7b2c 11.1 \u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3 OpenAPI \u89c4\u8303\u672c\u8eab\u4ee5\u53ca\u5b83\u5728 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u4f4d\u7f6e\u3002\u60a8\u5c06\u4e86\u89e3\u53ef\u7528\u4e8e\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u542f\u7528 OpenAPI \u6587\u6863\u751f\u6210\u7684\u5e93\uff0c\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u4e2d\u95f4\u4ef6\u516c\u5f00\u6587\u6863\u3002<\/p>\n<p>Once you have an OpenAPI document, you\u2019ll see how to do something useful with it in section 11.2, where we add Swagger UI to your app. Swagger UI uses your app\u2019s OpenAPI document to generate a UI for testing and inspecting the endpoints in your app, which can be especially useful for local testing.<\/p>\n<p>\u62e5\u6709 OpenAPI \u6587\u6863\u540e\uff0c\u60a8\u5c06\u5728\u7b2c 11.2 \u8282\u4e2d\u770b\u5230\u5982\u4f55\u4f7f\u7528\u5b83\u6267\u884c\u4e00\u4e9b\u6709\u7528\u7684\u4f5c\uff0c\u6211\u4eec\u5c06\u5728\u5176\u4e2d\u5c06 Swagger UI \u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002Swagger UI \u4f7f\u7528\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u6587\u6863\u751f\u6210\u7528\u4e8e\u6d4b\u8bd5\u548c\u68c0\u67e5\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u7aef\u70b9\u7684 UI\uff0c\u8fd9\u5bf9\u4e8e\u672c\u5730\u6d4b\u8bd5\u7279\u522b\u6709\u7528\u3002<\/p>\n<p>After seeing your app described in Swagger UI, it\u2019s time to head back to the code in section 11.3. OpenAPI and Swagger UI need rich metadata about your endpoints to provide the best functionality, so we look at some of the basic metadata you can add to your endpoints.<\/p>\n<p>\u5728 Swagger UI \u4e2d\u770b\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u63cf\u8ff0\u540e\uff0c\u662f\u65f6\u5019\u8fd4\u56de\u7b2c 11.3 \u8282\u4e2d\u7684\u4ee3\u7801\u4e86\u3002OpenAPI \u548c Swagger UI \u9700\u8981\u6709\u5173\u7ec8\u7aef\u8282\u70b9\u7684\u4e30\u5bcc\u5143\u6570\u636e\u624d\u80fd\u63d0\u4f9b\u6700\u4f73\u529f\u80fd\uff0c\u56e0\u6b64\u6211\u4eec\u6765\u770b\u770b\u60a8\u53ef\u4ee5\u6dfb\u52a0\u5230\u7ec8\u7aef\u8282\u70b9\u7684\u4e00\u4e9b\u57fa\u672c\u5143\u6570\u636e\u3002<\/p>\n<p>In section 11.4 you\u2019ll learn about one of the best tooling features that comes from creating an OpenAPI description of your app: automatically generated clients. Using a third-party library called NSwag, you\u2019ll learn how to automatically generate C# code and classes for interacting with your API based on the OpenAPI description you added in the previous sections. You\u2019ll learn how to generate your client, customize the generated code, and rebuild the client when your app\u2019s OpenAPI description changes.<\/p>\n<p>\u5728 Section 11.4 \u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u63cf\u8ff0\u6240\u5e26\u6765\u7684\u6700\u4f73\u5de5\u5177\u529f\u80fd\u4e4b\u4e00\uff1a\u81ea\u52a8\u751f\u6210\u7684\u5ba2\u6237\u7aef\u3002\u4f7f\u7528\u540d\u4e3a NSwag \u7684\u7b2c\u4e09\u65b9\u5e93\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u6839\u636e\u60a8\u5728\u524d\u9762\u90e8\u5206\u4e2d\u6dfb\u52a0\u7684 OpenAPI \u63cf\u8ff0\u81ea\u52a8\u751f\u6210\u7528\u4e8e\u4e0e API \u4ea4\u4e92\u7684 C# \u4ee3\u7801\u548c\u7c7b\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u751f\u6210\u5ba2\u6237\u7aef\u3001\u81ea\u5b9a\u4e49\u751f\u6210\u7684\u4ee3\u7801\uff0c\u4ee5\u53ca\u5728\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u63cf\u8ff0\u53d1\u751f\u66f4\u6539\u65f6\u91cd\u65b0\u6784\u5efa\u5ba2\u6237\u7aef\u3002<\/p>\n<p>Finally, in section 11.5, you\u2019ll learn more ways to add metadata to your endpoints to give the best experience for your generated clients. You\u2019ll learn how to add summaries and descriptions to your endpoints by using method calls and attributes and by extracting the XML documentation comments from your C# code.<\/p>\n<p>\u6700\u540e\uff0c\u5728\u7b2c 11.5 \u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5c06\u5143\u6570\u636e\u6dfb\u52a0\u5230\u7ec8\u7aef\u8282\u70b9\u7684\u66f4\u591a\u65b9\u6cd5\uff0c\u4ee5\u4fbf\u4e3a\u751f\u6210\u7684\u5ba2\u6237\u7aef\u63d0\u4f9b\u6700\u4f73\u4f53\u9a8c\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528\u65b9\u6cd5\u8c03\u7528\u548c\u5c5e\u6027\u4ee5\u53ca\u4ece C# \u4ee3\u7801\u4e2d\u63d0\u53d6 XML \u6587\u6863\u6ce8\u91ca\uff0c\u4ece\u800c\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u6458\u8981\u548c\u8bf4\u660e\u3002<\/p>\n<p>Before we consider those advanced scenarios, we\u2019ll look at the OpenAPI specification, what it is, and how you can add an OpenAPI document to your app.<\/p>\n<p>\u5728\u8003\u8651\u8fd9\u4e9b\u9ad8\u7ea7\u65b9\u6848\u4e4b\u524d\uff0c\u6211\u4eec\u5c06\u4e86\u89e3 OpenAPI \u89c4\u8303\u3001\u5b83\u662f\u4ec0\u4e48\u4ee5\u53ca\u5982\u4f55\u5c06 OpenAPI \u6587\u6863\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<h2>11.1 Adding an OpenAPI description to your app<\/h2>\n<h2>11.1 \u5c06 OpenAPI \u63cf\u8ff0\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f<\/h2>\n<p>OpenAPI (previously called Swagger) is a language-agnostic specification for describing RESTful APIs. At its core, OpenAPI describes the schema of a JavaScript Object Notation (JSON) document which in turn describes the URLs available in your application, how to invoke them, and the data types they return. In this section you\u2019ll learn how to generate an OpenAPI document for your minimal API application.<\/p>\n<p>OpenAPI\uff08\u4ee5\u524d\u79f0\u4e3a Swagger\uff09\u662f\u4e00\u79cd\u4e0e\u8bed\u8a00\u65e0\u5173\u7684\u89c4\u8303\uff0c\u7528\u4e8e\u63cf\u8ff0 RESTful API\u3002OpenAPI \u7684\u6838\u5fc3\u662f\u63cf\u8ff0 JavaScript \u5bf9\u8c61\u8868\u793a\u6cd5 \uff08JSON\uff09 \u6587\u6863\u7684\u67b6\u6784\uff0c\u800c JSON \u6587\u6863\u53c8\u63cf\u8ff0\u4e86\u5e94\u7528\u7a0b\u5e8f\u4e2d\u53ef\u7528\u7684 URL\u3001\u5982\u4f55\u8c03\u7528\u5b83\u4eec\u4ee5\u53ca\u5b83\u4eec\u8fd4\u56de\u7684\u6570\u636e\u7c7b\u578b\u3002\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4e3a\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u751f\u6210 OpenAPI \u6587\u6863\u3002<\/p>\n<p>Providing an OpenAPI document for your application makes it possible to add various types of automation for your app. You can do the following things, for example:<\/p>\n<p>\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b OpenAPI \u6587\u6863\u53ef\u4ee5\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0\u5404\u79cd\u7c7b\u578b\u7684\u81ea\u52a8\u5316\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u6267\u884c\u4ee5\u4e0b\u4f5c\uff1a<\/p>\n<ul>\n<li>\n<p>Explore your app using Swagger UI (section 11.2).<br \/>\n\u4f7f\u7528 Swagger UI \u6d4f\u89c8\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff08\u7b2c 11.2 \u8282\uff09\u3002<\/p>\n<\/li>\n<li>\n<p>Generate strongly-typed clients for interacting with your app (section 11.4).<br \/>\n\u751f\u6210\u7528\u4e8e\u4e0e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4ea4\u4e92\u7684\u5f3a\u7c7b\u578b\u5ba2\u6237\u7aef\uff08\u7b2c 11.4 \u8282\uff09\u3002<\/p>\n<\/li>\n<li>\n<p>Automatically integrate into third-party services such as Azure API Management<br \/>\n\u81ea\u52a8\u96c6\u6210\u5230\u7b2c\u4e09\u65b9\u670d\u52a1\u4e2d\uff0c\u4f8b\u5982 Azure API \u7ba1\u7406\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>Note<\/strong>  If you\u2019re familiar with SOAP from the old ASP.NET days, you can think of OpenAPI as being the HTTP\/REST equivalent of Web Service Description Language (WSDL). Just as a .wsdl file described your XML SOAP services, so the OpenAPI document describes your REST API.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5982\u679c\u60a8\u719f\u6089 ASP.NET \u5e74\u4ee3\u7684 SOAP\uff0c\u5219\u53ef\u4ee5\u5c06 OpenAPI \u89c6\u4e3a Web \u670d\u52a1\u63cf\u8ff0\u8bed\u8a00 \uff08WSDL\uff09 \u7684 HTTP\/REST \u7b49\u6548\u9879\u3002\u6b63\u5982 .wsdl \u6587\u4ef6\u63cf\u8ff0 XML SOAP \u670d\u52a1\u4e00\u6837\uff0cOpenAPI \u6587\u6863\u4e5f\u63cf\u8ff0\u4e86 REST API\u3002<\/p>\n<p>ASP.NET Core includes some support for OpenAPI documents out of the box, but to take advantage of them you\u2019ll need to use a third-party library. The two best-known libraries to use are called NSwag and Swashbuckle. In this chapter I use Swashbuckle to add an OpenAPI document to an ASP.NET Core app. You can read how to use NSwag instead at <a href=\"http:\/\/mng.bz\/6Dmy\">http:\/\/mng.bz\/6Dmy<\/a>.<\/p>\n<p>ASP.NET Core \u5305\u542b\u4e00\u4e9b\u5f00\u7bb1\u5373\u7528\u7684 OpenAPI \u6587\u6863\u652f\u6301\uff0c\u4f46\u8981\u5229\u7528\u5b83\u4eec\uff0c\u60a8\u9700\u8981\u4f7f\u7528\u7b2c\u4e09\u65b9\u5e93\u3002\u4e24\u4e2a\u6700\u8457\u540d\u7684\u5e93\u79f0\u4e3a NSwag \u548c Swashbuckle\u3002\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u5c06\u4f7f\u7528 Swashbuckle \u5c06 OpenAPI \u6587\u6863\u6dfb\u52a0\u5230 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/6Dmy\">http:\/\/mng.bz\/6Dmy<\/a> \u9605\u8bfb\u5982\u4f55\u4f7f\u7528 NSwag\u3002<\/p>\n<p><strong>Note<\/strong> NSwag and Swashbuckle provide similar functionality for generating OpenAPI documents, though you\u2019ll find slight differences in how to use them and in the features they support. NSwag also supports client generation, as you\u2019ll see in section 11.4.<br \/>\n<strong>\u6ce8\u610f<\/strong> NSwag \u548c Swashbuckle \u4e3a\u751f\u6210 OpenAPI \u6587\u6863\u63d0\u4f9b\u4e86\u7c7b\u4f3c\u7684\u529f\u80fd\uff0c\u4f46\u60a8\u4f1a\u53d1\u73b0\u5b83\u4eec\u7684\u4f7f\u7528\u65b9\u5f0f\u548c\u5b83\u4eec\u652f\u6301\u7684\u529f\u80fd\u7565\u6709\u4e0d\u540c\u3002NSwag \u8fd8\u652f\u6301\u5ba2\u6237\u7aef\u751f\u6210\uff0c\u60a8\u5c06\u5728 11.4 \u8282\u4e2d\u770b\u5230\u3002<\/p>\n<p>Add the Swashbuckle.AspNetCore NuGet package to your project by using the NuGet Package Manager in Visual Studio, or use the .NET CLI by running<br \/>\n\u4f7f\u7528 Visual Studio \u4e2d\u7684 NuGet \u5305\u7ba1\u7406\u5668\u5c06 Swashbuckle.AspNetCore NuGet \u5305\u6dfb\u52a0\u5230\u9879\u76ee\u4e2d\uff0c\u6216\u4f7f\u7528.NET CLI \u901a\u8fc7\u8fd0\u884c<\/p>\n<pre><code>dotnet add package Swashbuckle.AspNetCore<\/code><\/pre>\n<p>from your project\u2019s folder. Swashbuckle uses ASP.NET Core metadata services to retrieve information about all the endpoints in your application and to generate an OpenAPI document. Then this document is served by middleware provided by Swashbuckle, as shown in figure 11.1. Swashbuckle also includes middleware for visualizing your OpenAPI document, as you\u2019ll see in section 11.2.<\/p>\n<p>\u4ece\u9879\u76ee\u7684\u6587\u4ef6\u5939\u4e2d\u3002Swashbuckle \u4f7f\u7528 ASP.NET Core \u5143\u6570\u636e\u670d\u52a1\u6765\u68c0\u7d22\u6709\u5173\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6240\u6709\u7ec8\u7aef\u8282\u70b9\u7684\u4fe1\u606f\u5e76\u751f\u6210 OpenAPI \u6587\u6863\u3002\u7136\u540e\u8fd9\u4e2a\u6587\u6863\u7531 Swashbuckle \u63d0\u4f9b\u7684\u4e2d\u95f4\u4ef6\u63d0\u4f9b\uff0c\u5982\u56fe 11.1 \u6240\u793a\u3002Swashbuckle \u8fd8\u5305\u62ec\u7528\u4e8e\u53ef\u89c6\u5316 OpenAPI \u6587\u6863\u7684\u4e2d\u95f4\u4ef6\uff0c\u60a8\u5c06\u5728 11.2 \u8282\u4e2d\u770b\u5230\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1101.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.1 Swashbuckle uses ASP.NET Core metadata services to retrieve information about the endpoints in your application and builds an OpenAPI document. The OpenAPI middleware serves this document when requested. Swashbuckle also includes optional middleware for visualizing the OpenAPI document using Swagger UI.<br \/>\n\u56fe 11.1 Swashbuckle \u4f7f\u7528 ASP.NET Core \u5143\u6570\u636e\u670d\u52a1\u68c0\u7d22\u6709\u5173\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7ec8\u7aef\u8282\u70b9\u7684\u4fe1\u606f\u5e76\u6784\u5efa OpenAPI \u6587\u6863\u3002OpenAPI \u4e2d\u95f4\u4ef6\u5728\u8bf7\u6c42\u65f6\u63d0\u4f9b\u6b64\u6587\u6863\u3002Swashbuckle \u8fd8\u5305\u62ec\u7528\u4e8e\u4f7f\u7528 Swagger UI \u53ef\u89c6\u5316 OpenAPI \u6587\u6863\u7684\u53ef\u9009\u4e2d\u95f4\u4ef6\u3002<\/p>\n<p>After installing Swashbuckle, configure your application to generate an OpenAPI document as shown in listing 11.1. This listing shows a reduced version of the fruit API from chapter 5, with only the GET and POST methods included for simplicity. The OpenAPI-related additions are in bold.<\/p>\n<p>\u5b89\u88c5 Swashbuckle \u540e\uff0c\u914d\u7f6e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4ee5\u751f\u6210\u4e00\u4e2a OpenAPI \u6587\u6863\uff0c\u5982\u6e05\u5355 11.1 \u6240\u793a\u3002\u6b64\u6e05\u5355\u663e\u793a\u4e86\u7b2c 5 \u7ae0\u4e2d fruit API \u7684\u7b80\u5316\u7248\u672c\uff0c\u4e3a\u7b80\u5355\u8d77\u89c1\uff0c\u4ec5\u5305\u542b GET \u548c POST \u65b9\u6cd5\u3002\u4e0e OpenAPI \u76f8\u5173\u7684\u6dfb\u52a0\u5185\u5bb9\u4ee5\u7c97\u4f53\u663e\u793a\u3002<\/p>\n<p><strong>Note<\/strong> Swashbuckle uses the old Swagger nomenclature rather than OpenAPI in its method names. You should think of OpenAPI as the name of the specification and Swagger as the name of the tooling related to OpenAPI, as described in this post: <a href=\"http:\/\/mng.bz\/o18M\">http:\/\/mng.bz\/o18M<\/a>.<br \/>\n<strong>\u6ce8\u610f<\/strong> Swashbuckle \u5728\u5176\u65b9\u6cd5\u540d\u79f0\u4e2d\u4f7f\u7528\u65e7\u7684 Swagger \u547d\u540d\u6cd5\uff0c\u800c\u4e0d\u662f OpenAPI\u3002\u60a8\u5e94\u8be5\u5c06 OpenAPI \u89c6\u4e3a\u89c4\u8303\u7684\u540d\u79f0\uff0c\u5c06 Swagger \u89c6\u4e3a\u4e0e OpenAPI \u76f8\u5173\u7684\u5de5\u5177\u7684\u540d\u79f0\uff0c\u5982\u672c\u6587\u6240\u8ff0\uff1a<a href=\"http:\/\/mng.bz\/o18M\">http:\/\/mng.bz\/o18M<\/a>\u3002<\/p>\n<p>Listing 11.1 Adding OpenAPI support to a minimal API app using Swashbuckle<br \/>\n\u6e05\u5355 11.1 \u4f7f\u7528 Swashbuckle \u5411\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0 OpenAPI \u652f\u6301<\/p>\n<pre><code>using System.Collections.Concurrent;\n\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEndpointsApiExplorer(); \u2776\nbuilder.Services.AddSwaggerGen(); \u2777\n\nWebApplication app = builder.Build();\n\nvar _fruit = new ConcurrentDictionary&lt;string, Fruit&gt;();\n\napp.UseSwagger(); \u2778\napp.UseSwaggerUI(); \u2779\n\napp.MapGet(&quot;\/fruit\/{id}&quot;, (string id) =&gt;\n    _fruit.TryGetValue(id, out var fruit)\n        ? TypedResults.Ok(fruit)\n        : Results.Problem(statusCode: 404));\n\napp.MapPost(&quot;\/fruit\/{id}&quot;, (string id, Fruit fruit) =&gt;\n    _fruit.TryAdd(id, fruit)\n        ? TypedResults.Created($&quot;\/fruit\/{id}&quot;, fruit)\n        : Results.ValidationProblem(new Dictionary&lt;string, string[]&gt;\n            {\n             { &quot;id&quot;, new[] { &quot;A fruit with this id already exists&quot; } }\n        }));\n\napp.Run();\n\nrecord Fruit(string Name, int Stock);<\/code><\/pre>\n<p>\u2776 Adds the endpoint-discovery features of ASP.NET Core that Swashbuckle requires<br \/>\n\u6dfb\u52a0\u4e86 Swashbuckle \u6240\u9700\u7684 ASP.NET Core \u7684\u7aef\u70b9\u53d1\u73b0\u529f\u80fd<br \/>\n\u2777 Adds the Swashbuckle services required for creating OpenApi Documents<br \/>\n\u6dfb\u52a0\u4e86\u521b\u5efa OpenApi \u6587\u6863\u6240\u9700\u7684 Swashbuckle \u670d\u52a1<br \/>\n\u2778 Adds middleware to expose the OpenAPI document for your app<br \/>\n\u6dfb\u52a0\u4e2d\u95f4\u4ef6\u4ee5\u516c\u5f00\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u6587\u6863<br \/>\n\u2779 Adds middleware that serves the Swagger UI<br \/>\n\u6dfb\u52a0\u4e3a Swagger UI \u63d0\u4f9b\u670d\u52a1\u7684\u4e2d\u95f4\u4ef6<\/p>\n<p>With the changes in this listing, your application exposes an OpenAPI description of its endpoints. If you run the app and navigate to \/swagger\/v1\/swagger.json, you\u2019ll find a large JSON file, similar to the one shown in figure 11.2. This file is the OpenAPI Document description of your application.<\/p>\n<p>\u901a\u8fc7\u6b64\u6e05\u5355\u4e2d\u7684\u66f4\u6539\uff0c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5c06\u516c\u5f00\u5176\u7ec8\u7aef\u8282\u70b9\u7684 OpenAPI \u63cf\u8ff0\u3002\u5982\u679c\u60a8\u8fd0\u884c\u5e94\u7528\u7a0b\u5e8f\u5e76\u5bfc\u822a\u5230 \/swagger\/v1\/swagger.json\uff0c\u60a8\u5c06\u627e\u5230\u4e00\u4e2a\u5927\u578b JSON \u6587\u4ef6\uff0c\u7c7b\u4f3c\u4e8e\u56fe 11.2 \u4e2d\u6240\u793a\u7684\u6587\u4ef6\u3002\u6b64\u6587\u4ef6\u662f\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u6587\u6863\u63cf\u8ff0\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1102.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.2 The OpenAPI Document for the app described in listing 11.1, generated with NSwag.<br \/>\n\u56fe 11.2 \u6e05\u5355 11.1 \u4e2d\u63cf\u8ff0\u7684\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u6587\u6863\uff0c\u4f7f\u7528 NSwag \u751f\u6210<\/p>\n<p>The OpenAPI document includes a general description of your app, such as a title and version, as well as specific details about each of the endpoints. In figure 11.2, for example, the \/fruit\/{id} endpoint describes the fact that it needs a GET verb and takes an id parameter in the path.<\/p>\n<p>OpenAPI \u6587\u6863\u5305\u62ec\u5e94\u7528\u7a0b\u5e8f\u7684\u4e00\u822c\u63cf\u8ff0\uff0c\u4f8b\u5982\u6807\u9898\u548c\u7248\u672c\uff0c\u4ee5\u53ca\u6709\u5173\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u7684\u5177\u4f53\u8be6\u7ec6\u4fe1\u606f\u3002\u4f8b\u5982\uff0c\u5728\u56fe 11.2 \u4e2d\uff0c\/fruit\/{id} \u7aef\u70b9\u63cf\u8ff0\u5b83\u9700\u8981\u4e00\u4e2a GETverb \u5e76\u5728 path \u4e2d\u91c7\u7528 id \u53c2\u6570\u3002<\/p>\n<p>You can change some of the document values, such as the title, by adding configuration to the AddSwaggerGen() method. You can set the title of the app to &quot;Fruitify&quot; and add a description for the document:<\/p>\n<p>\u60a8\u53ef\u4ee5\u901a\u8fc7\u5411 AddSwaggerGen\uff08\uff09 \u65b9\u6cd5\u6dfb\u52a0\u914d\u7f6e\u6765\u66f4\u6539\u67d0\u4e9b\u6587\u6863\u503c\uff0c\u4f8b\u5982\u6807\u9898\u3002\u60a8\u53ef\u4ee5\u5c06\u5e94\u7528\u7a0b\u5e8f\u7684\u6807\u9898\u8bbe\u7f6e\u4e3a \u201cFruitify\u201d \u5e76\u4e3a\u6587\u6863\u6dfb\u52a0\u63cf\u8ff0\uff1a<\/p>\n<pre><code>builder.Services.AddSwaggerGen(x =&gt;\n    x.SwaggerDoc(&quot;v1&quot;, new OpenApiInfo()\n    {\n        Title = &quot;Fruitify&quot;,\n        Description = &quot;An API for interacting with fruit stock&quot;,\n        Version = &quot;1.0&quot;\n    }));<\/code><\/pre>\n<p>You can also change settings such as the path used to expose the document and various minutia about how Swashbuckle generates the final JSON. See the documentation for details: <a href=\"http:\/\/mng.bz\/OxQR\">http:\/\/mng.bz\/OxQR<\/a>.<\/p>\n<p>\u60a8\u8fd8\u53ef\u4ee5\u66f4\u6539\u8bbe\u7f6e\uff0c\u4f8b\u5982\u7528\u4e8e\u516c\u5f00\u6587\u6863\u7684\u8def\u5f84\u4ee5\u53ca\u6709\u5173 Swashbuckle \u5982\u4f55\u751f\u6210\u6700\u7ec8 JSON \u7684\u5404\u79cd\u7ec6\u8282\u3002\u6709\u5173\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u6587\u6863\uff1a<a href=\"http:\/\/mng.bz\/OxQR\">http:\/\/mng.bz\/OxQR<\/a>\u3002<\/p>\n<p>All that is clever, but if you\u2019re shrugging and asking \u201cSo what?\u201d, where OpenAPI really shines is the hooks it provides for other tooling. And you\u2019ve already added one such piece of tooling to your app: Swagger UI.<\/p>\n<p>\u6240\u6709\u8fd9\u4e9b\u90fd\u5f88\u806a\u660e\uff0c\u4f46\u5982\u679c\u4f60\u8038\u8038\u80a9\u95ee\u201c\u90a3\u53c8\u600e\u6837\u201d\uff0cOpenAPI \u771f\u6b63\u95ea\u8000\u7684\u5730\u65b9\u662f\u5b83\u4e3a\u5176\u4ed6\u5de5\u5177\u63d0\u4f9b\u7684\u94a9\u5b50\u3002\u60a8\u5df2\u7ecf\u5411\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0\u4e86\u8fd9\u6837\u4e00\u4e2a\u5de5\u5177\uff1aSwagger UI\u3002<\/p>\n<h2>11.2 Testing your APIs with Swagger UI<\/h2>\n<h2>11.2 \u4f7f\u7528 Swagger UI \u6d4b\u8bd5 API<\/h2>\n<p>In this section you\u2019ll learn about Swagger UI (<a href=\"https:\/\/swagger.io\/tools\/swagger-ui\">https:\/\/swagger.io\/tools\/swagger-ui<\/a>), an open-source web UI that makes it easy to visualize and test your OpenAPI apps. In some ways you can think of Swagger UI as being a light version of Postman, which I used in previous chapters to interact with minimal API applications. Swagger UI provides an easy way to view all the endpoints in your application and send requests to them. Postman provides many extra features, such as creating collections and sharing them with your team, but if all you\u2019re trying to do is test your application locally, Swagger UI is a great option.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3 Swagger UI \uff08<a href=\"https:\/\/swagger.io\/tools\/swagger-ui\uff09\uff0c\u8fd9\u662f\u4e00\u79cd\u5f00\u6e90\">https:\/\/swagger.io\/tools\/swagger-ui\uff09\uff0c\u8fd9\u662f\u4e00\u79cd\u5f00\u6e90<\/a> Web UI\uff0c\u53ef\u8ba9\u60a8\u8f7b\u677e\u53ef\u89c6\u5316\u548c\u6d4b\u8bd5 OpenAPI \u5e94\u7528\u7a0b\u5e8f\u3002\u5728\u67d0\u4e9b\u65b9\u9762\uff0c\u60a8\u53ef\u4ee5\u5c06 Swagger UI \u89c6\u4e3a Postman \u7684\u8f7b\u91cf\u7ea7\u7248\u672c\uff0c\u6211\u5728\u524d\u9762\u7684\u7ae0\u8282\u4e2d\u4f7f\u7528\u5b83\u6765\u4e0e\u4e4b\u4ea4\u4e92\u6700\u5c11\u7684 API \u5e94\u7528\u7a0b\u5e8f\u3002Swagger UI \u63d0\u4f9b\u4e86\u4e00\u79cd\u7b80\u5355\u7684\u65b9\u6cd5\u6765\u67e5\u770b\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709\u7ec8\u7aef\u8282\u70b9\u5e76\u5411\u5b83\u4eec\u53d1\u9001\u8bf7\u6c42\u3002Postman \u63d0\u4f9b\u4e86\u8bb8\u591a\u989d\u5916\u7684\u529f\u80fd\uff0c\u4f8b\u5982\u521b\u5efa\u96c6\u5408\u5e76\u4e0e\u60a8\u7684\u56e2\u961f\u5171\u4eab\u5b83\u4eec\uff0c\u4f46\u5982\u679c\u60a8\u53ea\u60f3\u5728\u672c\u5730\u6d4b\u8bd5\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u90a3\u4e48 Swagger UI \u662f\u4e00\u4e2a\u4e0d\u9519\u7684\u9009\u62e9\u3002<\/p>\n<p>You can add Swagger UI to your ASP.NET Core application using Swashbuckle by calling<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u8c03\u7528 Swashbuckle \u5c06 Swagger UI \u6dfb\u52a0\u5230\u60a8\u7684 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d<\/p>\n<pre><code>app.UseSwaggerUI()<\/code><\/pre>\n<p>to add the Swagger UI middleware, as you saw in listing 11.1. The Swagger UI middleware automatically integrates with the OpenAPI document middleware and exposes the Swagger UI web UI in your app at the path \/swagger by default. Navigate to \/swagger in your app, and you see a page like the one in figure 11.3.<\/p>\n<p>\u6dfb\u52a0 Swagger UI \u4e2d\u95f4\u4ef6\uff0c\u5982\u6e05\u5355 11.1 \u6240\u793a\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cSwagger UI \u4e2d\u95f4\u4ef6\u4f1a\u81ea\u52a8\u4e0e OpenAPI \u6587\u6863\u4e2d\u95f4\u4ef6\u96c6\u6210\uff0c\u5e76\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u8def\u5f84 \/swagger \u4e2d\u516c\u5f00 Swagger UI Web UI\u3002\u5bfc\u822a\u5230\/swagger \u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u60a8\u4f1a\u770b\u5230\u4e00\u4e2a\u7c7b\u4f3c\u4e8e\u56fe 11.3 \u4e2d\u7684\u9875\u9762\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1103.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.3 The Swagger UI endpoint for the app. With this UI you can view all the endpoints in your app, the schema of objects that are sent and returned, and even test the APIs by providing parameters and sending requests.<br \/>\n\u56fe 11.3 \u5e94\u7528\u7a0b\u5e8f\u7684 Swagger UI \u7aef\u70b9\u3002\u4f7f\u7528\u6b64 UI\uff0c\u60a8\u53ef\u4ee5\u67e5\u770b\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709\u7aef\u70b9\u3001\u5df2\u53d1\u9001\u548c\u8fd4\u56de\u7684\u5bf9\u8c61\u7684 schema\uff0c\u751a\u81f3\u901a\u8fc7\u63d0\u4f9b\u53c2\u6570\u548c\u53d1\u9001\u8bf7\u6c42\u6765\u6d4b\u8bd5 API\u3002<\/p>\n<p>Swagger UI lists all the endpoints described in the OpenAPI document, the schema of objects that are sent to and received from each API, and all the possible responses that each endpoint can return. You can even test an API from the UI by choosing Try it out, entering a value for the parameter, and choosing Execute. Swagger UI shows the command executed, the response headers, and the response body (figure 11.4).<\/p>\n<p>Swagger UI \u5217\u51fa\u4e86 OpenAPI \u6587\u6863\u4e2d\u63cf\u8ff0\u7684\u6240\u6709\u7aef\u70b9\u3001\u53d1\u9001\u5230\u6bcf\u4e2a API \u548c\u4ece\u6bcf\u4e2a API \u63a5\u6536\u7684\u5bf9\u8c61\u67b6\u6784\uff0c\u4ee5\u53ca\u6bcf\u4e2a\u7aef\u70b9\u53ef\u4ee5\u8fd4\u56de\u7684\u6240\u6709\u53ef\u80fd\u54cd\u5e94\u3002\u60a8\u751a\u81f3\u53ef\u4ee5\u4ece UI \u4e2d\u6d4b\u8bd5 API\uff0c\u65b9\u6cd5\u662f\u9009\u62e9 Try it out \uff08\u8bd5\u7528\uff09\uff0c\u8f93\u5165\u53c2\u6570\u503c\uff0c\u7136\u540e\u9009\u62e9 Execute \uff08\u6267\u884c\uff09\u3002Swagger UI \u663e\u793a\u6267\u884c\u7684\u547d\u4ee4\u3001\u54cd\u5e94\u6807\u5934\u548c\u54cd\u5e94\u6b63\u6587\uff08\u56fe 11.4\uff09\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1104.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.4 You can send requests using the Swagger UI by choosing an API, entering the required parameters, and choosing Execute. Swagger UI shows the response received.<br \/>\n\u56fe 11.4 \u60a8\u53ef\u4ee5\u901a\u8fc7\u9009\u62e9 API\u3001\u8f93\u5165\u6240\u9700\u53c2\u6570\u5e76\u9009\u62e9 Execute \uff08\u6267\u884c\uff09 \u6765\u4f7f\u7528 Swagger UI \u53d1\u9001\u8bf7\u6c42\u3002Swagger UI \u663e\u793a\u6536\u5230\u7684\u54cd\u5e94\u3002<\/p>\n<p>Swagger UI is a useful tool for exploring your APIs and can replace a tool like Postman in some cases. But the examples we\u2019ve shown so far reveal a problem with our API: the responses described for the GET endpoint in figure 11.3 mentioned a 200 response, but our execution in figure 11.4 reveals that it can also return a 404. To solve that documentation issue, we need to add extra metadata to our APIs.<\/p>\n<p>Swagger UI \u662f\u63a2\u7d22 API \u7684\u6709\u7528\u5de5\u5177\uff0c\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\u53ef\u4ee5\u53d6\u4ee3 Postman \u7b49\u5de5\u5177\u3002\u4f46\u662f\uff0c\u5230\u76ee\u524d\u4e3a\u6b62\u6211\u4eec\u5c55\u793a\u7684\u793a\u4f8b\u63ed\u793a\u4e86 API \u7684\u4e00\u4e2a\u95ee\u9898\uff1a\u56fe 11.3 \u4e2d\u4e3a GET \u7aef\u70b9\u63cf\u8ff0\u7684\u54cd\u5e94\u63d0\u5230\u4e86 200 \u54cd\u5e94\uff0c\u4f46\u6211\u4eec\u5728\u56fe 11.4 \u4e2d\u7684\u6267\u884c\u8868\u660e\u5b83\u4e5f\u53ef\u4ee5\u8fd4\u56de 404\u3002\u4e3a\u4e86\u89e3\u51b3\u8be5\u6587\u6863\u95ee\u9898\uff0c\u6211\u4eec\u9700\u8981\u5411 API \u6dfb\u52a0\u989d\u5916\u7684\u5143\u6570\u636e\u3002<\/p>\n<h3>11.3 Adding metadata to your minimal APIs<\/h3>\n<h3>11.3 \u5c06\u5143\u6570\u636e\u6dfb\u52a0\u5230\u6700\u5c0f API<\/h3>\n<p>Metadata is information about an API that doesn\u2019t change the execution of the API itself. You used metadata in chapter 5 when you added names to your endpoints, using WithName(), so that you could reference them by using LinkGenerator. The name doesn\u2019t change anything about how the endpoint executes, but it provides information for other features to hook into.<\/p>\n<p>\u5143\u6570\u636e\u662f\u6709\u5173 API \u7684\u4fe1\u606f\uff0c\u5b83\u4e0d\u4f1a\u66f4\u6539 API \u672c\u8eab\u7684\u6267\u884c\u3002\u5728\u7b2c 5 \u7ae0\u4e2d\uff0c\u5f53\u60a8\u4f7f\u7528 WithName\uff08\uff09 \u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u540d\u79f0\u65f6\uff0c\u60a8\u4f7f\u7528\u4e86\u5143\u6570\u636e\uff0c\u4ee5\u4fbf\u60a8\u53ef\u4ee5\u4f7f\u7528 LinkGenerator \u5f15\u7528\u5b83\u4eec\u3002\u8be5\u540d\u79f0\u4e0d\u4f1a\u66f4\u6539\u7ec8\u7aef\u8282\u70b9\u7684\u6267\u884c\u65b9\u5f0f\uff0c\u4f46\u5b83\u4e3a\u8981\u6302\u63a5\u7684\u5176\u4ed6\u529f\u80fd\u63d0\u4f9b\u4e86\u4fe1\u606f\u3002<\/p>\n<p>Currently, you can add three broad categories of metadata to minimal API endpoints:<\/p>\n<p>\u76ee\u524d\uff0c\u60a8\u53ef\u4ee5\u5c06\u4e09\u5927\u7c7b\u5143\u6570\u636e\u6dfb\u52a0\u5230\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\uff1a<\/p>\n<ul>\n<li>\n<p>Routing metadata\u2014As you\u2019ve already seen, the WithName() methods adds a globally unique name to an endpoint that\u2019s used for URL generation.<br \/>\n\u8def\u7531\u5143\u6570\u636e \u2013 \u5982\u60a8\u6240\u89c1\uff0cWithName\uff08\uff09 \u65b9\u6cd5\u5c06\u5168\u5c40\u552f\u4e00\u540d\u79f0\u6dfb\u52a0\u5230\u7528\u4e8e URL \u751f\u6210\u7684\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<\/li>\n<li>\n<p>Metadata for other middleware\u2014Several pieces of middleware can be customized on a per-request basis by adding metadata to an endpoint. When the middleware runs, it checks the selected endpoint\u2019s metadata and acts accordingly. Examples include authorization, hostname filtering, and output caching.<br \/>\n\u5176\u4ed6\u4e2d\u95f4\u4ef6\u7684\u5143\u6570\u636e \u2014 \u901a\u8fc7\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u5143\u6570\u636e\uff0c\u53ef\u4ee5\u6309\u8bf7\u6c42\u81ea\u5b9a\u4e49\u591a\u4e2a\u4e2d\u95f4\u4ef6\u3002\u5f53\u4e2d\u95f4\u4ef6\u8fd0\u884c\u65f6\uff0c\u5b83\u4f1a\u68c0\u67e5\u6240\u9009\u7ec8\u7aef\u8282\u70b9\u7684\u5143\u6570\u636e\u5e76\u91c7\u53d6\u76f8\u5e94\u7684\u884c\u52a8\u3002\u793a\u4f8b\u5305\u62ec\u6388\u6743\u3001\u4e3b\u673a\u540d\u7b5b\u9009\u548c\u8f93\u51fa\u7f13\u5b58\u3002<\/p>\n<\/li>\n<li>\n<p>OpenAPI metadata\u2014OpenAPI document generation is driven by the metadata exposed by endpoints, which in turn controls the UI exposed by Swagger UI.<br \/>\nOpenAPI \u5143\u6570\u636e - OpenAPI \u6587\u6863\u751f\u6210\u7531\u7ec8\u7aef\u8282\u70b9\u516c\u5f00\u7684\u5143\u6570\u636e\u9a71\u52a8\uff0c\u800c\u7ec8\u7aef\u8282\u70b9\u53c8\u63a7\u5236 Swagger UI \u516c\u5f00\u7684 UI\u3002<\/p>\n<\/li>\n<\/ul>\n<p>We look at how to add authorization metadata to your endpoints in chapter 25, so for now we\u2019ll focus on improving the OpenAPI description of your app using metadata. You can provide a lot of details to document your APIs, some of which Swashbuckle uses during OpenAPI generation and some of which it doesn\u2019t. The following listing shows how to add a tag for each API and how to explicitly describe the responses that are returned, using Produces().<\/p>\n<p>\u6211\u4eec\u5c06\u5728\u7b2c 25 \u7ae0\u4e2d\u4ecb\u7ecd\u5982\u4f55\u5c06\u6388\u6743\u5143\u6570\u636e\u6dfb\u52a0\u5230\u60a8\u7684\u7ec8\u7aef\u8282\u70b9\uff0c\u56e0\u6b64\u73b0\u5728\u6211\u4eec\u5c06\u4e13\u6ce8\u4e8e\u4f7f\u7528\u5143\u6570\u636e\u6539\u8fdb\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u63cf\u8ff0\u3002\u60a8\u53ef\u4ee5\u63d0\u4f9b\u5927\u91cf\u8be6\u7ec6\u4fe1\u606f\u6765\u8bb0\u5f55\u60a8\u7684 API\uff0c\u5176\u4e2d\u4e00\u4e9b Swashbuckle \u5728 OpenAPI \u751f\u6210\u671f\u95f4\u4f7f\u7528\uff0c\u800c\u53e6\u4e00\u4e9b\u5219\u4e0d\u4f7f\u7528\u3002\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u4e3a\u6bcf\u4e2a API \u6dfb\u52a0\u4e00\u4e2a\u6807\u7b7e\uff0c\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528 Produces\uff08\uff09 \u663e\u5f0f\u63cf\u8ff0\u8fd4\u56de\u7684\u54cd\u5e94\u3002<\/p>\n<p>Listing 11.2 Adding OpenAPI metadata to improve endpoint documentation<br \/>\n\u6e05\u5355 11.2 \u6dfb\u52a0 OpenAPI \u5143\u6570\u636e\u4ee5\u6539\u8fdb\u7aef\u70b9\u6587\u6863<\/p>\n<pre><code>using System.Collections.Concurrent;\n\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nWebApplication app = builder.Build();\n\nvar _fruit = new ConcurrentDictionary&lt;string, Fruit&gt;();\n\napp.UseSwagger();\napp.UseSwaggerUI();\n\napp.MapGet(&quot;\/fruit\/{id}&quot;, (string id) =&gt;\n    _fruit.TryGetValue(id, out var fruit)\n        ? TypedResults.Ok(fruit)\n        : Results.Problem(statusCode: 404))\n    .WithTags(&quot;fruit&quot;) \u2776\n    .Produces&lt;Fruit&gt;() \u2777\n    .ProducesProblem(404); \u2778\n\napp.MapPost(&quot;\/fruit\/{id}&quot;, (string id, Fruit fruit) =&gt;\n    _fruit.TryAdd(id, fruit)\n        ? TypedResults.Created($&quot;\/fruit\/{id}&quot;, fruit)\n        : Results.ValidationProblem(new Dictionary&lt;string, string[]&gt;\n            {\n            { &quot;id&quot;, new[] { &quot;A fruit with this id already exists&quot; } }\n            }))\n    .WithTags(&quot;fruit&quot;) \u2779\n    .Produces&lt;Fruit&gt;(201) \u277a\n    .ProducesValidationProblem(); \u277b\n\napp.Run();\nrecord Fruit(string Name, int stock);\n<\/code><\/pre>\n<p>\u2776 Adding a tag groups the endpoints in Swagger UI. Each endpoint can have multiple<br \/>\ntags.<br \/>\n\u6dfb\u52a0\u6807\u7b7e\u5bf9 Swagger UI \u4e2d\u7684\u7aef\u70b9\u8fdb\u884c\u5206\u7ec4\u3002\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u53ef\u4ee5\u6709\u591a\u4e2a\u6807\u7b7e\u3002<br \/>\n\u2777 The endpoint can return a Fruit object. When not specified, a 200 response is<br \/>\nassumed.<br \/>\n\u7aef\u70b9\u53ef\u4ee5\u8fd4\u56de Fruit \u5bf9\u8c61\u3002\u5982\u679c\u672a\u6307\u5b9a\uff0c\u5219\u5047\u5b9a\u54cd\u5e94\u4e3a 200\u3002<br \/>\n\u2778 If the id isn\u2019t found, the endpoint returns a 404 Problem Details response.<br \/>\n\u5982\u679c\u672a\u627e\u5230 ID\uff0c\u5219\u7ec8\u7aef\u8282\u70b9\u5c06\u8fd4\u56de 404 Problem Details \u54cd\u5e94\u3002<br \/>\n\u2779 Adding a tag groups the endpoints in Swagger UI. Each endpoint can have multiple<br \/>\ntags.<br \/>\n\u5728 Swagger UI \u4e2d\u6dfb\u52a0\u6807\u7b7e\u5bf9\u7aef\u70b9\u8fdb\u884c\u5206\u7ec4\u3002\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u53ef\u4ee5\u6709\u591a\u4e2a\u6807\u7b7e\u3002<br \/>\n\u277a This endpoint also returns a Fruit object but uses a 201 response instead of 200.<br \/>\n\u6b64\u7aef\u70b9\u8fd8\u8fd4\u56de\u4e00\u4e2a Fruit \u5bf9\u8c61\uff0c\u4f46\u4f7f\u7528 201 \u54cd\u5e94\u800c\u4e0d\u662f 200\u3002<br \/>\n\u277b If the ID already exists, it returns a 400 Problem Details response with validation<br \/>\nerrors.<br \/>\n\u5982\u679c ID \u5df2\u5b58\u5728\uff0c\u5219\u8fd4\u56de 400 Problem Details \u54cd\u5e94\uff0c\u5176\u4e2d\u5305\u542b\u9a8c\u8bc1\u9519\u8bef\u3002<\/p>\n<p>With these changes, Swagger UI shows the correct responses for each endpoint, as shown in figure 11.5. It also groups the endpoints under the tag &quot;fruit&quot; instead of the default tag inferred from the project name when no tags are provided.<\/p>\n<p>\u901a\u8fc7\u8fd9\u4e9b\u66f4\u6539\uff0cSwagger UI \u4f1a\u4e3a\u6bcf\u4e2a\u7aef\u70b9\u663e\u793a\u6b63\u786e\u7684\u54cd\u5e94\uff0c\u5982\u56fe 11.5 \u6240\u793a\u3002\u5b83\u8fd8\u5c06\u7ec8\u7aef\u8282\u70b9\u5206\u7ec4\u5728\u6807\u7b7e \u201cfruit\u201d \u4e0b\uff0c\u800c\u4e0d\u662f\u5728\u672a\u63d0\u4f9b\u6807\u7b7e\u65f6\u4ece\u9879\u76ee\u540d\u79f0\u63a8\u65ad\u7684\u9ed8\u8ba4\u6807\u7b7e\u4e0b\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1105.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.5 Swagger UI groups the endpoints in your application based on the Tag metadata attached to them. The UI uses the metadata added by calling Produces() to document the expected return types and status codes for each endpoint.<br \/>\n\u56fe 11.5 Swagger UI \u6839\u636e\u9644\u52a0\u5230\u7ec8\u7aef\u8282\u70b9\u7684 Tag \u5143\u6570\u636e\u5bf9\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u7ec8\u7aef\u8282\u70b9\u8fdb\u884c\u5206\u7ec4\u3002UI \u4f7f\u7528\u901a\u8fc7\u8c03\u7528 Produces\uff08\uff09 \u6dfb\u52a0\u7684\u5143\u6570\u636e\u6765\u8bb0\u5f55\u6bcf\u4e2a\u7aef\u70b9\u7684\u9884\u671f\u8fd4\u56de\u7c7b\u578b\u548c\u72b6\u6001\u4ee3\u7801\u3002<\/p>\n<p>If adding all this extra metadata feels like a bit of a chore, don\u2019t worry. Adding the extra OpenAPI metadata is optional, necessary only if you plan to expose your OpenAPI document for others to consume. If all you want is an easy way to test your minimal APIs, you can go a long way without many of these extra method calls.<\/p>\n<p>\u5982\u679c\u6dfb\u52a0\u6240\u6709\u8fd9\u4e9b\u989d\u5916\u7684\u5143\u6570\u636e\u611f\u89c9\u6709\u70b9\u9ebb\u70e6\uff0c\u8bf7\u4e0d\u8981\u62c5\u5fc3\u3002\u6dfb\u52a0\u989d\u5916\u7684 OpenAPI \u5143\u6570\u636e\u662f\u53ef\u9009\u7684\uff0c\u53ea\u6709\u5f53\u60a8\u8ba1\u5212\u516c\u5f00 OpenAPI \u6587\u6863\u4f9b\u5176\u4ed6\u4eba\u4f7f\u7528\u65f6\uff0c\u624d\u9700\u8981\u6dfb\u52a0\u5143\u6570\u636e\u3002\u5982\u679c\u4f60\u60f3\u8981\u7684\u53ea\u662f\u4e00\u79cd\u7b80\u5355\u7684\u65b9\u6cd5\u6765\u6d4b\u8bd5\u4f60\u7684\u6700\u5c0fAPI\uff0c\u60a8\u53ef\u4ee5\u5728\u6ca1\u6709\u8bb8\u591a\u989d\u5916\u65b9\u6cd5\u8c03\u7528\u7684\u60c5\u51b5\u4e0b\u8d70\u5f88\u957f\u7684\u8def\u3002<\/p>\n<p><strong>Tip<\/strong> Remember that you can also use route groups (described in chapter 5) to apply metadata to multiple APIs at the same time.<br \/>\n<strong>\u63d0\u793a<\/strong> \u8bf7\u8bb0\u4f4f\uff0c\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u7ec4\uff08\u5982\u7b2c 5 \u7ae0\u6240\u8ff0\uff09\u540c\u65f6\u5c06\u5143\u6570\u636e\u5e94\u7528\u4e8e\u591a\u4e2a API\u3002<\/p>\n<p>One of the strongest arguments for making your OpenAPI descriptions as rich as possible is that it makes the tooling around your API easier to use. Swagger UI is one example. But an arguably even more useful tool lets you automatically generate C# clients for interacting with your APIs.<\/p>\n<p>\u4f7f OpenAPI \u63cf\u8ff0\u5c3d\u53ef\u80fd\u4e30\u5bcc\u7684\u6700\u6709\u529b\u7684\u8bba\u636e\u4e4b\u4e00\u662f\uff0c\u5b83\u4f7f\u56f4\u7ed5 API \u7684\u5de5\u5177\u66f4\u6613\u4e8e\u4f7f\u7528\u3002Swagger UI \u5c31\u662f\u4e00\u4e2a\u4f8b\u5b50\u3002\u4f46\u662f\uff0c\u4e00\u4e2a\u53ef\u4ee5\u8bf4\u66f4\u6709\u7528\u7684\u5de5\u5177\u5141\u8bb8\u60a8\u81ea\u52a8\u751f\u6210\u7528\u4e8e\u4e0e API \u4ea4\u4e92\u7684 C# \u5ba2\u6237\u7aef\u3002<\/p>\n<h2>11.4 Generating strongly typed clients with NSwag<\/h2>\n<h2>11.4 \u4f7f\u7528 NSwag \u751f\u6210\u5f3a\u7c7b\u578b\u5ba2\u6237\u7aef<\/h2>\n<p>In this section you\u2019ll learn how to use your OpenAPI description to automatically generate a client class that you can use to call your API from another C# project. You\u2019ll create a console application, use a .NET tool to generate a C# client for interacting with your API, and finally customize the generated types. The generated code includes automatic serialization and deserialization of request types, and makes interacting with your API from another C# project much easier than the alternative method of crafting HTTP requests manually.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528 OpenAPI \u63cf\u8ff0\u81ea\u52a8\u751f\u6210\u53ef\u7528\u4e8e\u4ece\u53e6\u4e00\u4e2a C# \u9879\u76ee\u8c03\u7528 API \u7684\u5ba2\u6237\u7aef\u7c7b\u3002\u60a8\u5c06\u521b\u5efa\u4e00\u4e2a\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f7f\u7528 .NET \u5de5\u5177\u751f\u6210\u7528\u4e8e\u4e0e API \u4ea4\u4e92\u7684 C# \u5ba2\u6237\u7aef\uff0c\u6700\u540e\u81ea\u5b9a\u4e49\u751f\u6210\u7684\u7c7b\u578b\u3002\u751f\u6210\u7684\u4ee3\u7801\u5305\u62ec\u8bf7\u6c42\u7c7b\u578b\u7684\u81ea\u52a8\u5e8f\u5217\u5316\u548c\u53cd\u5e8f\u5217\u5316\uff0c\u5e76\u4e14\u4e0e\u624b\u52a8\u521b\u5efa HTTP \u8bf7\u6c42\u7684\u66ff\u4ee3\u65b9\u6cd5\u76f8\u6bd4\uff0c\u4ece\u53e6\u4e00\u4e2a C# \u9879\u76ee\u4e0e API \u4ea4\u4e92\u8981\u5bb9\u6613\u5f97\u591a\u3002<\/p>\n<p><strong>Note<\/strong> Generating a strongly typed client is optional. It makes it easier to consume your APIs from C#, but if you don\u2019t need this functionality, you can still test your APIs by using Postman or another HTTP client.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u751f\u6210\u5f3a\u7c7b\u578b\u5ba2\u6237\u7aef\u662f\u53ef\u9009\u7684\u3002\u5b83\u4f7f\u4ece C# \u4f7f\u7528 API \u53d8\u5f97\u66f4\u52a0\u5bb9\u6613\uff0c\u4f46\u5982\u679c\u60a8\u4e0d\u9700\u8981\u6b64\u529f\u80fd\uff0c\u60a8\u4ecd\u7136\u53ef\u4ee5\u4f7f\u7528 Postman \u6216\u5176\u4ed6 HTTP \u5ba2\u6237\u7aef\u6765\u6d4b\u8bd5 API\u3002<\/p>\n<p>You could use any of several tools to automatically generate a C# client from an OpenAPI description, such as OpenAPI Generator (<a href=\"http:\/\/mng.bz\/Y1wB\">http:\/\/mng.bz\/Y1wB<\/a>), but in this chapter I use NSwag. You may remember from section 11.1 that NSwag can be used instead of Swashbuckle to generate an OpenAPI description for your API. But unlike Swashbuckle, NSwag also contains a client generator. NSwag is also the default library used by both Visual Studio and the Microsoft .NET OpenAPI global tool to generate C# client code.<\/p>\n<p>\u60a8\u53ef\u4ee5\u4f7f\u7528\u591a\u79cd\u5de5\u5177\u4e2d\u7684\u4efb\u4f55\u4e00\u79cd\u4ece OpenAPI \u63cf\u8ff0\u81ea\u52a8\u751f\u6210 C# \u5ba2\u6237\u7aef\uff0c\u4f8b\u5982 OpenAPIGenerator \uff08<a href=\"http:\/\/mng.bz\/Y1wB\uff09\uff0c\u4f46\u5728\u672c\u7ae0\u4e2d\u6211\u4f7f\u7528\">http:\/\/mng.bz\/Y1wB\uff09\uff0c\u4f46\u5728\u672c\u7ae0\u4e2d\u6211\u4f7f\u7528<\/a> NSwag\u3002\u4f60\u53ef\u80fd\u8fd8\u8bb0\u5f97 11.1 \u8282 \u4e2d\uff0c\u53ef\u4ee5\u4f7f\u7528 NSwag \u4ee3\u66ff Swashbuckle \u6765\u4e3a\u4f60\u7684 API \u751f\u6210 OpenAPI \u63cf\u8ff0\u3002\u4f46\u4e0e Swashbuckle \u4e0d\u540c\u7684\u662f\uff0cNSwag \u8fd8\u5305\u542b\u4e00\u4e2a\u5ba2\u6237\u7aef\u751f\u6210\u5668\u3002NSwag \u4e5f\u662f Visual Studio \u548c Microsoft .NET OpenAPI \u5168\u5c40\u5de5\u5177\u7528\u6765\u751f\u6210 C# \u5ba2\u6237\u7aef\u4ee3\u7801\u7684\u9ed8\u8ba4\u5e93\u3002<\/p>\n<p>Code generation based on an OpenAPI description works via the process shown in figure 11.6. First, Visual Studio or the .NET tool downloads the OpenAPI description JSON file so that it\u2019s available locally. The code generation tool reads the OpenAPI description, identifies all the endpoints and schemas described by the document, and generates a C# client class that you can use to call the API described in the document. The code generation tool hooks into the build process so that any time the local OpenAPI description file changes, the code generator runs to regenerate the client.<\/p>\n<p>\u57fa\u4e8e OpenAPI \u63cf\u8ff0\u7684\u4ee3\u7801\u751f\u6210\u5de5\u4f5c\u8fc7\u7a0b\u5982\u56fe 11.6 \u6240\u793a\u3002\u9996\u5148\uff0cVisual Studio \u6216 .NET \u5de5\u5177\u4e0b\u8f7d OpenAPI \u63cf\u8ff0 JSON \u6587\u4ef6\uff0c\u4ee5\u4fbf\u5b83\u5728\u672c\u5730\u53ef\u7528\u3002\u4ee3\u7801\u751f\u6210\u5de5\u5177\u8bfb\u53d6 OpenAPI \u63cf\u8ff0\uff0c\u8bc6\u522b\u6587\u6863\u63cf\u8ff0\u7684\u6240\u6709\u7aef\u70b9\u548c\u67b6\u6784\uff0c\u5e76\u751f\u6210\u4e00\u4e2a C# \u5ba2\u6237\u7aef\u7c7b\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8be5\u7c7b\u6765\u8c03\u7528\u6587\u6863\u4e2d\u63cf\u8ff0\u7684 API\u3002\u4ee3\u7801\u751f\u6210\u5de5\u5177\u6302\u63a5\u5230\u6784\u5efa\u8fc7\u7a0b\u4e2d\uff0c\u56e0\u6b64\uff0c\u6bcf\u5f53\u672c\u5730 OpenAPI \u63cf\u8ff0\u6587\u4ef6\u53d1\u751f\u66f4\u6539\u65f6\uff0c\u4ee3\u7801\u751f\u6210\u5668\u90fd\u4f1a\u8fd0\u884c\u4ee5\u91cd\u65b0\u751f\u6210\u5ba2\u6237\u7aef\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1106.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.6 Visual Studio or a .NET tool downloads the OpenAPI description locally and installs the code-generation tool from NuGet. When your project builds, the generation tool reads the OpenAPI description and generates a C# class for interacting with the API.<br \/>\n\u56fe 11.6 Visual Studio \u6216 .NET \u5de5\u5177\u5728\u672c\u5730\u4e0b\u8f7d OpenAPI \u63cf\u8ff0\uff0c\u5e76\u4ece NuGet \u5b89\u88c5\u4ee3\u7801\u751f\u6210\u5de5\u5177\u3002\u751f\u6210\u9879\u76ee\u65f6\uff0c\u751f\u6210\u5de5\u5177\u4f1a\u8bfb\u53d6 OpenAPI \u63cf\u8ff0\u5e76\u751f\u6210\u7528\u4e8e\u4e0e API \u4ea4\u4e92\u7684 C# \u7c7b\u3002<\/p>\n<p>You can generate clients by using Visual Studio, as shown in section 11.4.1, or a .NET tool, as shown in section 11.4.2. Both approaches produce the same result, so your choice is a matter of personal preference.<\/p>\n<p>\u60a8\u53ef\u4ee5\u4f7f\u7528 Visual Studio\uff08\u5982\u7b2c 11.4.1 \u8282\u6240\u793a\uff09\u6216 .NET \u5de5\u5177\uff08\u5982\u7b2c 11.4.2 \u8282\u6240\u793a\uff09\u751f\u6210\u5ba2\u6237\u7aef\u3002\u8fd9\u4e24\u79cd\u65b9\u6cd5\u90fd\u4f1a\u4ea7\u751f\u76f8\u540c\u7684\u7ed3\u679c\uff0c\u56e0\u6b64\u60a8\u7684\u9009\u62e9\u53d6\u51b3\u4e8e\u4e2a\u4eba\u559c\u597d\u3002<\/p>\n<h3>11.4.1 Generating a client using Visual Studio<\/h3>\n<h3>11.4.1 \u4f7f\u7528 Visual Studio \u751f\u6210\u5ba2\u6237\u7aef<\/h3>\n<p>In this section I show how to generate a client by using Visual Studio\u2019s built-in support. For this section I assume that you have a simple .NET 7 console app that needs to interact with your minimal API app.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u5982\u4f55\u4f7f\u7528 Visual Studio \u7684\u5185\u7f6e\u652f\u6301\u751f\u6210\u5ba2\u6237\u7aef\u3002\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u6211\u5047\u8bbe\u4f60\u6709\u4e00\u4e2a\u7b80\u5355\u7684 .NET 7 \u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\uff0c\u5b83\u9700\u8981\u4e0e\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u4ea4\u4e92\u3002<\/p>\n<p><strong>Note<\/strong> In the sample code for this chapter, both applications are in the same solution for simplicity, but they don\u2019t need to be. You don\u2019t even need the source code for the API; as long as you have the OpenAPI description of an API, you can generate a client for it.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5728\u672c\u7ae0\u7684\u793a\u4f8b\u4ee3\u7801\u4e2d\uff0c\u4e3a\u7b80\u5355\u8d77\u89c1\uff0c\u8fd9\u4e24\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4f4d\u4e8e\u540c\u4e00\u89e3\u51b3\u65b9\u6848\u4e2d\uff0c\u4f46\u5e76\u975e\u5fc5\u987b\u5982\u6b64\u3002\u60a8\u751a\u81f3\u4e0d\u9700\u8981 API \u7684\u6e90\u4ee3\u7801;\u53ea\u8981\u60a8\u5177\u6709 API \u7684 OpenAPI \u63cf\u8ff0\uff0c\u5c31\u53ef\u4ee5\u4e3a\u5176\u751f\u6210\u5ba2\u6237\u7aef\u3002<\/p>\n<p>To generate the client, follow these steps:<br \/>\n\u8981\u751f\u6210\u5ba2\u6237\u7aef\uff0c\u8bf7\u6267\u884c\u4ee5\u4e0b\u6b65\u9aa4\uff1a<\/p>\n<ol>\n<li>\n<p>Ensure that the API application is running and that the OpenAPI description JSON file is accessible. Note the URL at which the JSON file is exposed. If you\u2019re following along with the source code for the book, run the OpenApiExample project.<br \/>\n\u786e\u4fdd API \u5e94\u7528\u7a0b\u5e8f\u6b63\u5728\u8fd0\u884c\uff0c\u5e76\u4e14 OpenAPI \u63cf\u8ff0 JSON \u6587\u4ef6\u53ef\u8bbf\u95ee\u3002\u8bb0\u4e0b\u516c\u5f00 JSON \u6587\u4ef6\u7684 URL\u3002\u5982\u679c\u60a8\u6309\u7167\u672c\u4e66\u7684\u6e90\u4ee3\u7801\u8fdb\u884c\u4f5c\uff0c\u8bf7\u8fd0\u884c OpenApiExample \u9879\u76ee\u3002<\/p>\n<\/li>\n<li>\n<p>In the client project, right-click the project file and then choose from the contextual menu, as shown in figure 11.7. This command opens the Add Service Reference dialog box.<br \/>\n\u5728\u5ba2\u6237\u7aef\u9879\u76ee\u4e2d\uff0c\u53f3\u952e\u5355\u51fb\u9879\u76ee\u6587\u4ef6\uff0c\u7136\u540e\u4ece\u4e0a\u4e0b\u6587\u83dc\u5355\u4e2d\u9009\u62e9 Add &gt; Service Reference\uff0c\u5982\u56fe 11.7 \u6240\u793a\u3002\u6b64\u547d\u4ee4\u5c06\u6253\u5f00 Add Service Reference \u5bf9\u8bdd\u6846\u3002<\/p>\n<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1107.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.7 Adding a service reference using Visual Studio. Right-click the project that will call the API, and choose Add &gt; Service Reference.<br \/>\n\u56fe 11.7 \u4f7f\u7528 Visual Studio \u6dfb\u52a0\u670d\u52a1\u5f15\u7528\u3002\u53f3\u952e\u5355\u51fb\u5c06\u8c03\u7528 API \u7684\u9879\u76ee\uff0c\u7136\u540e\u9009\u62e9 Add &gt; Service Reference\u3002<\/p>\n<ol start=\"3\">\n<li>In the Add Service Reference dialog box, select OpenAPI and then choose Next. On the Add New OpenAPI Service Reference page, enter the URL where the OpenAPI document is located. Enter a namespace for the generated code and a name for the generated client class, as shown in figure 11.8, and then choose Finish.<br \/>\n\u5728 Add Service Reference \uff08\u6dfb\u52a0\u670d\u52a1\u5f15\u7528\uff09 \u5bf9\u8bdd\u6846\u4e2d\uff0c\u9009\u62e9 OpenAPI \uff08OpenAPI\uff09\uff0c\u7136\u540e\u9009\u62e9 Next \uff08\u4e0b\u4e00\u6b65\uff09\u3002\u5728 Add New OpenAPI Service Reference \u9875\u9762\u4e0a\uff0c\u8f93\u5165 OpenAPI \u6587\u6863\u6240\u5728\u7684 URL\u3002\u8f93\u5165\u751f\u6210\u7684\u4ee3\u7801\u7684\u547d\u540d\u7a7a\u95f4\u548c\u751f\u6210\u7684\u5ba2\u6237\u7aef\u7c7b\u7684\u540d\u79f0\uff0c\u5982\u56fe 11.8 \u6240\u793a\uff0c\u7136\u540e\u9009\u62e9 Finish\u3002<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1108.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 11.8 Adding an OpenAPI service reference using Visual Studio. Add the link to the OpenAPI document, the code generation parameters, and click Finish. Visual Studio downloads the OpenAPI document and saves it to the project to use for code generation.<br \/>\n\u56fe 11.8 \u4f7f\u7528 Visual Studio \u6dfb\u52a0 OpenAPI \u670d\u52a1\u5f15\u7528\u3002\u5c06\u94fe\u63a5\u6dfb\u52a0\u5230 OpenAPI document\uff0c\u4ee3\u7801\u751f\u6210\u53c2\u6570\uff0c\u7136\u540e\u5355\u51fb Finish\u3002Visual Studio \u4e0b\u8f7d OpenAPI \u6587\u6863\u5e76\u5c06\u5176\u4fdd\u5b58\u5230\u9879\u76ee\u4e2d\u4ee5\u7528\u4e8e\u4ee3\u7801\u751f\u6210\u3002<\/p>\n<p>The Service Reference Configuration Progress screen shows the changes Visual Studio makes to your application, such as installing various NuGet packages and downloading the OpenAPI document.<br \/>\nService Reference Configuration Progress \u5c4f\u5e55\u663e\u793a Visual Studio \u5bf9\u5e94\u7528\u7a0b\u5e8f\u6240\u505a\u7684\u66f4\u6539\uff0c\u4f8b\u5982\u5b89\u88c5\u5404\u79cd NuGet \u5305\u548c\u4e0b\u8f7d OpenAPI \u6587\u6863\u3002<\/p>\n<p><strong>Tip<\/strong> If you\u2019re running the sample code with Visual Studio, you can find the OpenAPI document at <a href=\"https:\/\/localhost:7186\/swagger\/v1\/swagger.json\">https:\/\/localhost:7186\/swagger\/v1\/swagger.json<\/a>. This location is also displayed in the Swagger UI.<br \/>\n<strong>\u63d0\u793a<\/strong> \u5982\u679c\u4f7f\u7528 Visual Studio \u8fd0\u884c\u793a\u4f8b\u4ee3\u7801\uff0c\u5219\u53ef\u4ee5\u5728 <a href=\"https:\/\/localhost:7186\/swagger\/v1\/swagger.json\">https:\/\/localhost:7186\/swagger\/v1\/swagger.json<\/a> \u4e2d\u627e\u5230 OpenAPI \u6587\u6863\u3002\u6b64\u4f4d\u7f6e\u4e5f\u663e\u793a\u5728 Swagger UI \u4e2d\u3002<\/p>\n<p>After performing these steps, look at the csproj file of your console app. You\u2019ll see that several NuGet package references were added, as well as a new <OpenApiReference> element, as shown in listing 11.3.<\/p>\n<p>\u6267\u884c\u8fd9\u4e9b\u6b65\u9aa4\u540e\uff0c\u8bf7\u67e5\u770b\u63a7\u5236\u53f0\u5e94\u7528\u7684 csproj \u6587\u4ef6\u3002\u60a8\u5c06\u770b\u5230\u6dfb\u52a0\u4e86\u591a\u4e2a NuGet \u5305\u5f15\u7528\uff0c\u4ee5\u53ca\u4e00\u4e2a\u65b0\u7684 <OpenApiReference> \u5143\u7d20\uff0c\u5982\u6e05\u5355 11.3 \u6240\u793a\u3002<\/p>\n<p>Listing 11.3 Adding a service reference for OpenAPI client generation with Visual Studio<br \/>\n\u6e05\u5355 11.3 \u4e3a\u4f7f\u7528 Visual Studio \u751f\u6210 OpenAPI \u5ba2\u6237\u7aef\u6dfb\u52a0\u670d\u52a1\u5f15\u7528<\/p>\n<pre><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&gt;\n    &lt;PropertyGroup&gt;\n        &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\n        &lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;\n        &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n        &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n    &lt;\/PropertyGroup&gt;\n\n    &lt;ItemGroup&gt;\n        &lt;OpenApiReference \u2776\n            Include=&quot;OpenAPIs\\swagger.json&quot;\n            CodeGenerator=&quot;NSwagCSharp&quot;\n            Namespace=&quot;Fruit&quot;\n            ClassName=&quot;FruitClient&quot;&gt;\n        &lt;SourceUri&gt;https:\/\/localhost:7186\/swagger\/v1\/swagger.json&lt;\/SourceUri&gt;\n        &lt;\/OpenApiReference&gt;\n    &lt;\/ItemGroup&gt;\n\n    &lt;ItemGroup&gt;\n        &lt;PackageReference \u2777\n            Include=&quot;Microsoft.Extensions.ApiDescription.Client&quot;\n            Version=&quot;3.0.0&quot;&gt;\n            &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;\n            &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers;\n                buildtransitive&lt;\/IncludeAssets&gt;\n        &lt;\/PackageReference&gt;\n\n        &lt;PackageReference Include=&quot;Newtonsoft.Json&quot; Version=&quot;13.0.1&quot; \/&gt;\n\n        &lt;PackageReference Include=&quot;NSwag.ApiDescription.Client&quot;\n            Version=&quot;13.0.5&quot;&gt;\n                &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;\n                &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers;\n                    buildtransitive&lt;\/IncludeAssets&gt;\n        &lt;\/PackageReference&gt;\n    &lt;\/ItemGroup&gt;\n&lt;\/Project&gt;\n<\/code><\/pre>\n<p>\u2776 Defines where the OpenAPI description was loaded from and code generation settings<br \/>\n\u5b9a\u4e49 OpenAPI \u63cf\u8ff0\u7684\u52a0\u8f7d\u4f4d\u7f6e\u548c\u4ee3\u7801\u751f\u6210\u8bbe\u7f6e<br \/>\n\u2777 Extra NuGet packages are required by the code generator.<br \/>\n\u4ee3\u7801\u751f\u6210\u5668\u9700\u8981\u989d\u5916\u7684 NuGet \u5305\u3002<\/p>\n<p>Theoretically, this code should be everything you need to generate the client. Unfortunately, Visual Studio adds some out-of-date packages that you\u2019ll need to update before your project will build, as follows:<br \/>\n\u4ece\u7406\u8bba\u4e0a\u8bb2\uff0c\u6b64\u4ee3\u7801\u5e94\u8be5\u662f\u751f\u6210\u5ba2\u6237\u7aef\u6240\u9700\u7684\u4e00\u5207\u3002\u9057\u61be\u7684\u662f\uff0cVisual Studio \u6dfb\u52a0\u4e86\u4e00\u4e9b\u8fc7\u65f6\u7684\u5305\uff0c\u60a8\u9700\u8981\u5728\u6784\u5efa\u9879\u76ee\u4e4b\u524d\u66f4\u65b0\u8fd9\u4e9b\u5305\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<ol>\n<li>\n<p>Update NSwag.ApiDescription.Client to the latest version (currently, 13.18.2). This package does the code generation based on the OpenAPI description.<br \/>\n\u5c06 NSwag.ApiDescription.Client \u66f4\u65b0\u5230\u6700\u65b0\u7248\u672c\uff08\u5f53\u524d\u4e3a 13.18.2\uff09\u3002\u6b64\u5305\u6839\u636e OpenAPI \u63cf\u8ff0\u6267\u884c\u4ee3\u7801\u751f\u6210\u3002<\/p>\n<\/li>\n<li>\n<p>Update Microsoft.Extensions.ApiDescription.Client to the latest version (7.0.0 at the time of the .NET 7 release). This package is referenced transitively by NSwag.ApiDescription.Client anyway, so you don\u2019t have to reference it directly, but doing so ensures that you have the latest version of the package.<br \/>\n\u5c06 Microsoft.Extensions.ApiDescription.Client \u66f4\u65b0\u5230\u6700\u65b0\u7248\u672c\uff08.NET 7 \u7248\u672c\u53d1\u5e03\u65f6\u4e3a 7.0.0\uff09\u3002\u65e0\u8bba\u5982\u4f55\uff0c\u6b64\u5305\u90fd\u7531 NSwag.ApiDescription.Client \u4ee5\u4f20\u9012\u65b9\u5f0f\u5f15\u7528\uff0c\u56e0\u6b64\u60a8\u4e0d\u5fc5\u76f4\u63a5\u5f15\u7528\u5b83\uff0c\u4f46\u8fd9\u6837\u505a\u53ef\u4ee5\u786e\u4fdd\u60a8\u62e5\u6709\u6700\u65b0\u7248\u672c\u7684\u5305\u3002<\/p>\n<\/li>\n<\/ol>\n<p><strong>NOTE<\/strong> By default, the generated client uses Newtonsoft.Json to serializes the requests and responses. In section 11.4.4 you\u2019ll see how to replace it with the built-in System.Text.Json.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u751f\u6210\u7684\u5ba2\u6237\u7aef\u4f7f\u7528 Newtonsoft.Json \u6765\u5e8f\u5217\u5316\u8bf7\u6c42\u548c\u54cd\u5e94\u3002\u5728\u7b2c 11.4.4 \u8282\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u5c06\u5176\u66ff\u6362\u4e3a\u5185\u7f6e\u7684 System.Text.Json\u3002<\/p>\n<p>After you make these changes, your project should look similar to the following listing.<br \/>\n\u8fdb\u884c\u8fd9\u4e9b\u66f4\u6539\u540e\uff0c\u60a8\u7684\u9879\u76ee\u5e94\u7c7b\u4f3c\u4e8e\u4e0b\u9762\u7684\u6e05\u5355\u3002<\/p>\n<p>Listing 11.4 Updating package versions for OpenAPI generation<br \/>\n\u6e05\u5355 11.4 \u66f4\u65b0\u5305\u7248\u672c\u4ee5\u751f\u6210 OpenAPI<\/p>\n<pre><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&gt;\n\n  &lt;PropertyGroup&gt;\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\n    &lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n  &lt;\/PropertyGroup&gt;\n  &lt;ItemGroup&gt;\n    &lt;OpenApiReference\n        Include=&quot;OpenAPIs\\swagger.json&quot;\n        CodeGenerator=&quot;NSwagCSharp&quot;\n        Namespace=&quot;Fruit&quot;\n        ClassName=&quot;FruitClient&quot;&gt;\n      &lt;SourceUri&gt;https:\/\/localhost:7186\/swagger\/v1\/swagger.json&lt;\/SourceUri&gt;\n    &lt;\/OpenApiReference&gt;\n  &lt;\/ItemGroup&gt;\n\n  &lt;ItemGroup&gt;\n    &lt;PackageReference\n      Include=&quot;Microsoft.Extensions.ApiDescription.Client&quot;\n      Version=&quot;7.0.0&quot;&gt;  \u2776\n        &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;\n        &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers;\n          buildtransitive&lt;\/IncludeAssets&gt;\n    &lt;\/PackageReference&gt;\n    &lt;PackageReference Include=&quot;Newtonsoft.Json&quot; Version=&quot;13.0.1&quot; \/&gt;\n    &lt;PackageReference Include=&quot;NSwag.ApiDescription.Client&quot;\n      Version=&quot;13.18.2&quot;&gt;  \u2776\n        &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;\n        &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers;\n          buildtransitive&lt;\/IncludeAssets&gt;\n    &lt;\/PackageReference&gt;\n  &lt;\/ItemGroup&gt;\n\n&lt;\/Project&gt;<\/code><\/pre>\n<p>\u2776 Updates to the latest version<br \/>\n\u66f4\u65b0\u5230\u6700\u65b0\u7248\u672c<\/p>\n<p>With the packages updated, you can build your project and generate the FruitClient. In section 11.4.3 you\u2019ll see how to use this client to call your API, but first we\u2019ll look at how to generate the client with a .NET global tool if you\u2019re not using Visual Studio.<\/p>\n<p>\u66f4\u65b0\u5305\u540e\uff0c\u60a8\u53ef\u4ee5\u6784\u5efa\u9879\u76ee\u5e76\u751f\u6210 FruitClient\u3002\u5728 11.4.3 \u8282\u4e2d\uff0c\u60a8\u5c06\u770b\u5230\u5982\u4f55\u4f7f\u7528\u6b64\u5ba2\u6237\u7aef\u6765\u8c03\u7528\u60a8\u7684 API\uff0c\u4f46\u9996\u5148\u6211\u4eec\u5c06\u4e86\u89e3\u5982\u4f55\u5982\u679c\u60a8\u4f7f\u7528 Visual Studio\uff0c\u8bf7\u4f7f\u7528 .NET \u5168\u5c40\u5de5\u5177\u751f\u6210\u5ba2\u6237\u7aef\u3002<\/p>\n<h3>11.4.2 Generating a client using the .NET Global tool<\/h3>\n<h3>11.4.2 \u4f7f\u7528.NET \u5168\u5c40\u5de5\u5177<\/h3>\n<p>In this section you\u2019ll learn how to generate a client from an OpenAPI definition by using a .NET global tool instead of Visual Studio. The result is essentially the same, so if you\u2019ve followed the steps in section 11.4.1 in Visual Studio, you can skip this section.<\/p>\n<p>\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528 .NET \u5168\u5c40\u5de5\u5177\u800c\u4e0d\u662f Visual Studio \u4ece OpenAPI \u5b9a\u4e49\u751f\u6210\u5ba2\u6237\u7aef\u3002\u7ed3\u679c\u57fa\u672c\u76f8\u540c\uff0c\u56e0\u6b64\uff0c\u5982\u679c\u60a8\u5df2\u6309\u7167 Visual Studio \u4e2d 11.4.1 \u8282\u4e2d\u7684\u6b65\u9aa4\u4f5c\uff0c\u5219\u53ef\u4ee5\u8df3\u8fc7\u6b64\u90e8\u5206\u3002<\/p>\n<p><strong>Note<\/strong> You don\u2019t have to use Visual Studio or a .NET tool. Ultimately ,you need a csproj file that looks like listing 11.4 and an OpenAPI definition JSON file in your project, so if you\u2019re happy editing the project file and downloading the definition manually, you can take that approach. Visual Studio and the .NET tool simplify and automate some of these steps.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u60a8\u4e0d\u5fc5\u4f7f\u7528 Visual Studio \u6216 .NET \u5de5\u5177\u3002\u6700\u540e\uff0c\u60a8\u9700\u8981\u4e00\u4e2a\u7c7b\u4f3c\u4e8e\u6e05\u5355 11.4 \u7684 csproj \u6587\u4ef6\u548c\u4e00\u4e2a\u9879\u76ee\u4e2d\u7684 OpenAPI \u5b9a\u4e49 JSON \u6587\u4ef6\uff0c\u56e0\u6b64\uff0c\u5982\u679c\u60a8\u613f\u610f\u7f16\u8f91\u9879\u76ee\u6587\u4ef6\u5e76\u624b\u52a8\u4e0b\u8f7d\u5b9a\u4e49\uff0c\u5219\u53ef\u4ee5\u91c7\u7528\u8fd9\u79cd\u65b9\u6cd5\u3002Visual Studio \u548c .NET \u5de5\u5177\u7b80\u5316\u5e76\u81ea\u52a8\u6267\u884c\u5176\u4e2d\u4e00\u4e9b\u6b65\u9aa4\u3002<\/p>\n<p>As in section 11.4.1, the instructions in 11.4.2 assume that you have a console app that needs to call your API, that the API is accessible, and that it has an OpenAPI description. To generate a client by using NSwag, follow these steps:<\/p>\n<p>\u4e0e\u7b2c 11.4.1 \u8282\u4e00\u6837\uff0c11.4.2 \u4e2d\u7684\u8bf4\u660e\u5047\u5b9a\u60a8\u6709\u4e00\u4e2a\u9700\u8981\u8c03\u7528 API \u7684\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\uff0c\u8be5 API \u662f\u53ef\u8bbf\u95ee\u7684\uff0c\u5e76\u4e14\u5b83\u5177\u6709 OpenAPI \u63cf\u8ff0\u3002\u8981\u4f7f\u7528 NSwag \u751f\u6210\u5ba2\u6237\u7aef\uff0c\u8bf7\u6267\u884c\u4ee5\u4e0b\u6b65\u9aa4\uff1a<\/p>\n<ol>\n<li>\n<p>Ensure that the API application is running and that the OpenAPI description JSON file is accessible. Note the URL at which the JSON file is exposed. In the source code associated with the book, run the OpenApiExample project.<br \/>\n\u786e\u4fdd API \u5e94\u7528\u7a0b\u5e8f\u6b63\u5728\u8fd0\u884c\uff0c\u5e76\u4e14 OpenAPI \u63cf\u8ff0 JSON \u6587\u4ef6\u53ef\u8bbf\u95ee\u3002\u8bb0\u4e0b\u516c\u5f00 JSON \u6587\u4ef6\u7684 URL\u3002\u5728\u4e0e\u672c\u4e66\u5173\u8054\u7684\u6e90\u4ee3\u7801\u4e2d\uff0c\u8fd0\u884c OpenApiExample \u9879\u76ee\u3002<\/p>\n<\/li>\n<li>\n<p>Install the .NET OpenAPI tool (<a href=\"http:\/\/mng.bz\/GyOv\">http:\/\/mng.bz\/GyOv<\/a>) globally by running<br \/>\n\u901a\u8fc7\u8fd0\u884c .NET OpenAPI \u5de5\u5177 \uff08<a href=\"http:\/\/mng.bz\/GyOv\">http:\/\/mng.bz\/GyOv<\/a>\uff09 \u5168\u5c40\u5b89\u88c5<\/p>\n<\/li>\n<\/ol>\n<pre><code>dotnet tool install -g Microsoft.dotnet-openapi<\/code><\/pre>\n<ol start=\"3\">\n<li>From the project folder of your console app, add an OpenAPI reference by using the following command, substituting the path to the OpenAPI document and the location to download the JSON file to:<br \/>\n\u5728\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u7684\u9879\u76ee\u6587\u4ef6\u5939\u4e2d\uff0c\u4f7f\u7528\u4ee5\u4e0b\u547d\u4ee4\u6dfb\u52a0 OpenAPI \u5f15\u7528\uff0c\u5c06 OpenAPI \u6587\u6863\u7684\u8def\u5f84\u548c\u5c06 JSON \u6587\u4ef6\u4e0b\u8f7d\u5230\u7684\u4f4d\u7f6e\u66ff\u6362\u4e3a\uff1a<\/p>\n<pre><code>dotnet openapi add url http:\/\/localhost:5062\/swagger\/v1\/swagger.json --output-file OpenAPIs\\fruit.json<\/code><\/pre>\n<\/li>\n<\/ol>\n<p><strong>Tip<\/strong> If you\u2019re running the sample code by using dotnet run, you can find the OpenAPI document at the preceding URL. This location is also displayed in the Swagger UI.<br \/>\n<strong>\u63d0\u793a<\/strong> \u5982\u679c\u4f7f\u7528 dotnet run \u8fd0\u884c\u793a\u4f8b\u4ee3\u7801\uff0c\u5219\u53ef\u4ee5\u5728\u524d\u9762\u7684 URL \u4e2d\u627e\u5230 OpenAPI \u6587\u6863\u3002\u6b64\u4f4d\u7f6e\u4e5f\u663e\u793a\u5728 Swagger UI \u4e2d\u3002<\/p>\n<ol start=\"4\">\n<li>Update the packages added to your project by running the following commands from the project folder:<br \/>\n\u901a\u8fc7\u4ece\u9879\u76ee\u6587\u4ef6\u5939\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\u6765\u66f4\u65b0\u6dfb\u52a0\u5230\u9879\u76ee\u4e2d\u7684\u5305\uff1a<\/li>\n<\/ol>\n<pre><code>dotnet add package NSwag.ApiDescription.Client\ndotnet add package Microsoft.Extensions.ApiDescription.Client\ndotnet add package Newtonsoft.Json<\/code><\/pre>\n<p>After you run all these steps, your OpenAPI description file should have been downloaded to OpenAPIs\\fruit.json, and your project file should look similar to the following listing (elements added by the tool highlighted in bold).<\/p>\n<p>\u8fd0\u884c\u6240\u6709\u8fd9\u4e9b\u6b65\u9aa4\u540e\uff0c\u60a8\u7684 OpenAPI \u63cf\u8ff0\u6587\u4ef6\u5e94\u5df2\u4e0b\u8f7d\u5230 OpenAPIs\\fruit.json\uff0c\u5e76\u4e14\u60a8\u7684\u9879\u76ee\u6587\u4ef6\u5e94\u7c7b\u4f3c\u4e8e\u4ee5\u4e0b\u6e05\u5355\uff08\u7531\u8be5\u5de5\u5177\u6dfb\u52a0\u7684\u5143\u7d20\u4ee5\u7c97\u4f53\u7a81\u51fa\u663e\u793a\uff09\u3002<\/p>\n<p>Listing 11.5 Adding an OpenAPI reference using the .NET OpenAPI tool<br \/>\n\u6e05\u5355 11.5 \u4f7f\u7528 .NET OpenAPI \u5de5\u5177\u6dfb\u52a0 OpenAPI \u5f15\u7528<\/p>\n<pre><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&gt;\n\n  &lt;PropertyGroup&gt;\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\n    &lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n  &lt;\/PropertyGroup&gt;\n\n  &lt;ItemGroup&gt;\n    &lt;PackageReference\n      Include=&quot;Microsoft.Extensions.ApiDescription.Client&quot;\n      Version=&quot;7.0.0&quot;&gt;\n        &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers;\n          buildtransitive&lt;\/IncludeAssets&gt;\n      &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;\n    &lt;\/PackageReference&gt;\n    &lt;PackageReference Include=&quot;Newtonsoft.Json&quot; Version=&quot;13.0.1&quot; \/&gt;\n    &lt;PackageReference Include=&quot;NSwag.ApiDescription.Client&quot;\n      Version=&quot;13.18.2&quot;&gt;\n        &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers;\n          buildtransitive&lt;\/IncludeAssets&gt;\n      &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;\n    &lt;\/PackageReference&gt;\n  &lt;\/ItemGroup&gt;\n\n  &lt;ItemGroup&gt;\n    &lt;OpenApiReference Include=&quot;OpenAPIs\\fruit.json&quot;\n      SourceUrl=&quot;http:\/\/localhost:5062\/swagger\/v1\/swagger.json&quot; \/&gt;\n  &lt;\/ItemGroup&gt;\n&lt;\/Project&gt;<\/code><\/pre>\n<p>Other than minor ordering differences, the main difference between the Visual Studio approach and the .NET tool approach is that Visual Studio lets you specify the class name and namespace for your new client, whereas the .NET Tool uses the default values. For consistency, add the ClassName and Namespace attributes to the <code>&lt;OpenApiReference&gt;<\/code> element added by the tool:<\/p>\n<p>\u9664\u4e86\u7ec6\u5fae\u7684\u6392\u5e8f\u5dee\u5f02\u5916\uff0cVisual Studio \u65b9\u6cd5\u548c .NET \u5de5\u5177\u65b9\u6cd5\u4e4b\u95f4\u7684\u4e3b\u8981\u533a\u522b\u5728\u4e8e\uff0cVisual Studio \u5141\u8bb8\u60a8\u4e3a\u65b0\u5ba2\u6237\u7aef\u6307\u5b9a\u7c7b\u540d\u548c\u547d\u540d\u7a7a\u95f4\uff0c\u800c .NET \u5de5\u5177\u4f7f\u7528\u9ed8\u8ba4\u503c\u3002\u4e3a\u4e86\u4fdd\u6301\u4e00\u81f4\u6027\uff0c\u8bf7\u5c06 ClassName \u548c Namespace \u5c5e\u6027\u6dfb\u52a0\u5230\u5de5\u5177\u6dfb\u52a0\u7684 <code>&lt;OpenApiReference&gt;<\/code> \u5143\u7d20\u4e2d\uff1a<\/p>\n<pre><code>&lt;OpenApiReference Include=&quot;OpenAPIs\\fruit.json&quot;\n  SourceUrl=&quot;http:\/\/localhost:5062\/swagger\/v1\/swagger.json&quot;\n  Namespace=&quot;Fruit&quot;\n  ClassName=&quot;FruitClient&quot; \/&gt;<\/code><\/pre>\n<p>In section 11.4.4 you\u2019ll learn how to customize the generated code further, but before we get to that topic, let\u2019s look at the generated FruitClient and how to use it.<\/p>\n<p>\u5728 Section 11.4.4 \u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u8fdb\u4e00\u6b65\u81ea\u5b9a\u4e49\u751f\u6210\u7684\u4ee3\u7801\uff0c\u4f46\u5728\u6211\u4eec\u8fdb\u5165\u8be5\u4e3b\u9898\u4e4b\u524d\uff0c\u8ba9\u6211\u4eec\u770b\u770b\u751f\u6210\u7684 FruitClient \u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u5b83\u3002<\/p>\n<h3>11.4.3 Using a generated client to call your API<\/h3>\n<h3>11.4.3 \u4f7f\u7528\u751f\u6210\u7684\u5ba2\u6237\u7aef\u8c03\u7528 API<\/h3>\n<p>So far, you\u2019ve been taking my word for it that a client is magically generated for your application, so in this section you get to try it out. The NSwag.ApiDescription.Client package added to your project works with the Microsoft.Extensions.ApiDescription.Client package to read the OpenAPI description file in your project. From this description it can work out what APIs you have and what types you need to serialize to and from. Finally, it outputs a C# class with the class name and namespace you specified in the OpenApiReference element.<\/p>\n<p>\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u60a8\u4e00\u76f4\u76f8\u4fe1\u6211\u7684\u8bdd\uff0c\u5373 Client \u662f\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u795e\u5947\u751f\u6210\u7684\uff0c\u56e0\u6b64\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5feb\u6765\u8bd5\u8bd5\u5427\u3002\u6dfb\u52a0\u5230\u9879\u76ee\u7684 NSwag.ApiDescription.Client \u5305\u4e0e Microsoft.Extensions.ApiDescription.Client \u5305\u914d\u5408\u4f7f\u7528\uff0c\u4ee5\u8bfb\u53d6\u9879\u76ee\u4e2d\u7684 OpenAPI \u63cf\u8ff0\u6587\u4ef6\u3002\u4ece\u6b64\u63cf\u8ff0\u4e2d\uff0c\u5b83\u53ef\u4ee5\u8ba1\u7b97\u51fa\u60a8\u62e5\u6709\u54ea\u4e9b API \u4ee5\u53ca\u9700\u8981\u5e8f\u5217\u5316\u5230\u54ea\u4e9b\u7c7b\u578b\u6216\u4ece\u4e2d\u5e8f\u5217\u5316\u3002\u6700\u540e\uff0c\u5b83\u8f93\u51fa\u4e00\u4e2a C# \u7c7b\uff0c\u5176\u4e2d\u5305\u542b\u60a8\u5728 OpenApiReference \u5143\u7d20\u4e2d\u6307\u5b9a\u7684\u7c7b\u540d\u548c\u547d\u540d\u7a7a\u95f4\u3002<\/p>\n<p><strong>Note<\/strong> The generated file is typically saved to your project\u2019s obj folder. After building your project, you can find the fruitClient.cs file in this folder. Alternatively, use Visual Studio\u2019s Go To Definition (F12) functionality on an instance of FruitClient to navigate to the code in your integrated development environment (IDE).<br \/>\n<strong>\u6ce8\u610f<\/strong> \u751f\u6210\u7684\u6587\u4ef6\u901a\u5e38\u4fdd\u5b58\u5230\u9879\u76ee\u7684 obj \u6587\u4ef6\u5939\u4e2d\u3002\u6784\u5efa\u9879\u76ee\u540e\uff0c\u60a8\u53ef\u4ee5\u5728\u6b64\u6587\u4ef6\u5939\u4e2d\u627e\u5230 fruitClient.cs \u6587\u4ef6\u3002\u6216\u8005\uff0c\u5728 FruitClient \u5b9e\u4f8b\u4e0a\u4f7f\u7528 Visual Studio \u7684\u8f6c\u5230\u5b9a\u4e49 \uff08F12\uff09 \u529f\u80fd\u5bfc\u822a\u5230\u96c6\u6210\u5f00\u53d1\u73af\u5883 \uff08IDE\uff09 \u4e2d\u7684\u4ee3\u7801\u3002<\/p>\n<p>To use the FruitClient to call your API, you must create an instance of it, passing in the base address of your API and an HttpClient instance. Then you can send HTTP requests to the discovered endpoints. A client generated from the OpenAPI description of the simple minimal API in listing 11.2, for example, would have methods called FruitPOSTAsync() and FruitGETASync(), corresponding to the two exposed methods, as shown in listing 11.6.<\/p>\n<p>\u8981\u4f7f\u7528 FruitClient \u8c03\u7528 API\uff0c\u60a8\u5fc5\u987b\u521b\u5efa\u4e00\u4e2a API \u5b9e\u4f8b\uff0c\u5e76\u4f20\u5165 API \u7684\u57fa\u5740\u548c HttpClient \u5b9e\u4f8b\u3002\u7136\u540e\uff0c\u60a8\u53ef\u4ee5\u5411\u53d1\u73b0\u7684\u7ec8\u7aef\u8282\u70b9\u53d1\u9001 HTTP \u8bf7\u6c42\u3002\u4f8b\u5982\uff0c\u4ece\u6e05\u5355 11.2 \u4e2d\u7b80\u5355\u6700\u5c0f API \u7684 OpenAPI \u63cf\u8ff0\u751f\u6210\u7684\u5ba2\u6237\u7aef\u5c06\u5177\u6709\u540d\u4e3a FruitPOSTAsync\uff08\uff09 \u548c FruitGETASync\uff08\uff09 \u7684\u65b9\u6cd5\uff0c\u5bf9\u5e94\u4e8e\u4e24\u4e2a\u516c\u5f00\u7684\u65b9\u6cd5\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 11.6 Calling the API from listing 11.2 using a generated client<br \/>\n\u6e05\u5355 11.6 \u4f7f\u7528\u751f\u6210\u7684\u5ba2\u6237\u7aef\u8c03\u7528 \u6e05\u5355 11.2 \u4e2d\u7684 API<\/p>\n<pre><code>using Fruit; \u2776\n\nvar client = new FruitClient( \u2777\n    &quot;https:\/\/localhost:7186&quot;, \u2778\n    new HttpClient()); \u2779\n\nFruit.Fruit created = await client.FruitPOSTAsync(&quot;123&quot;, \u277a\n    new Fruit.Fruit { Name = &quot;Banana&quot;, Stock = 100 }); \u277b\nConsole.WriteLine($&quot;Created {created.Name}&quot;);\n\nFruit.Fruit fetched = await client.FruitGETAsync(&quot;123&quot;); \u277c\nConsole.WriteLine($&quot;Fetched {fetched.Name}&quot;);<\/code><\/pre>\n<p>\u2776 The code is generated in the Fruit namespace.<br \/>\n\u4ee3\u7801\u5728 Fruit \u547d\u540d\u7a7a\u95f4\u4e2d\u751f\u6210\u3002<br \/>\n\u2777 Uses the generated FruitClient<br \/>\n\u4f7f\u7528\u751f\u6210\u7684 FruitClient<br \/>\n\u2778 Specifies the base address of the API<br \/>\n\u6307\u5b9a API \u7684\u57fa\u5740<br \/>\n\u2779 The provided HttpClient is used to call the API.<br \/>\n\u63d0\u4f9b\u7684 HttpClient \u7528\u4e8e\u8c03\u7528 API\u3002<br \/>\n\u277a Calls the MapPost endpoint of the API<br \/>\n\u8c03\u7528 API \u7684 MapPost \u7aef\u70b9<br \/>\n\u277b The Fruit type is generated automatically by NSwag.<br \/>\nFruit \u7c7b\u578b\u7531 NSwag \u81ea\u52a8\u751f\u6210\u3002<br \/>\n\u277c Calls the MapGet endpoint of the API<br \/>\n\u8c03\u7528 API \u7684 MapGet \u7aef\u70b9<\/p>\n<p>This code is simultaneously impressive and somewhat horrible:<\/p>\n<p>\u8fd9\u6bb5\u4ee3\u7801\u65e2\u4ee4\u4eba\u5370\u8c61\u6df1\u523b\uff0c\u53c8\u6709\u4e9b\u53ef\u6015\uff1a<\/p>\n<ul>\n<li>\n<p>It\u2019s impressive that you\u2019re able to generate all the boilerplate code for interacting with the API. You don\u2019t have to do any string interpolation to calculate the path. You don\u2019t have to serialize the request body or deserialize the response. You don\u2019t have to check for error status codes. The generated code takes care of all those tasks.<br \/>\n\u4ee4\u4eba\u5370\u8c61\u6df1\u523b\u7684\u662f\uff0c\u60a8\u80fd\u591f\u751f\u6210\u7528\u4e8e\u4e0e API \u4ea4\u4e92\u7684\u6240\u6709\u6837\u677f\u4ee3\u7801\u3002\u60a8\u4e0d\u5fc5\u6267\u884c\u4efb\u4f55\u5b57\u7b26\u4e32\u63d2\u503c\u6765\u8ba1\u7b97\u8def\u5f84\u3002\u60a8\u4e0d\u5fc5\u5e8f\u5217\u5316\u8bf7\u6c42\u6b63\u6587\u6216\u53cd\u5e8f\u5217\u5316\u54cd\u5e94\u3002\u60a8\u4e0d\u5fc5\u68c0\u67e5\u9519\u8bef\u72b6\u6001\u4ee3\u7801\u3002\u751f\u6210\u7684\u4ee3\u7801\u4f1a\u5904\u7406\u6240\u6709\u8fd9\u4e9b\u4efb\u52a1\u3002<\/p>\n<\/li>\n<li>\n<p>Those FruitPOSTAsync and FruitGETAsync methods have really ugly names!<br \/>\n\u90a3\u4e9b FruitPOSTAsync \u548c FruitGETAsync \u65b9\u6cd5\u7684\u540d\u5b57\u771f\u7684\u5f88\u4e11\u964b\uff01<\/p>\n<\/li>\n<\/ul>\n<p>Luckily, you can fix the ugly method names: improve your API\u2019s OpenAPI definition by adding WithName() to every API. The name you provide for your endpoint is used as the OperationID in the OpenAPI description; then NSwag uses it to generate the client methods. This scenario is a prime example of adding more metadata to your OpenAPI, making the tooling better for your consumers.<\/p>\n<p>\u5e78\u8fd0\u7684\u662f\uff0c\u60a8\u53ef\u4ee5\u4fee\u590d\u4e11\u964b\u7684\u65b9\u6cd5\u540d\u79f0\uff1a\u901a\u8fc7\u5411\u6bcf\u4e2a API \u6dfb\u52a0 WithName\uff08\uff09 \u6765\u6539\u8fdb API \u7684 OpenAPI \u5b9a\u4e49\u3002\u60a8\u4e3a\u7ec8\u7aef\u8282\u70b9\u63d0\u4f9b\u7684\u540d\u79f0\u5c06\u7528\u4f5c OpenAPI \u63cf\u8ff0\u4e2d\u7684 OperationID;\u7136\u540e NSwag \u4f7f\u7528\u5b83\u6765\u751f\u6210\u5ba2\u6237\u7aef\u65b9\u6cd5\u3002\u6b64\u65b9\u6848\u662f\u5411 OpenAPI \u6dfb\u52a0\u66f4\u591a\u5143\u6570\u636e\u7684\u4e00\u4e2a\u5178\u578b\u793a\u4f8b\uff0c\u4f7f\u5de5\u5177\u66f4\u9002\u5408\u60a8\u7684\u4f7f\u7528\u8005\u3002<\/p>\n<p>As well as improve your OpenAPI description, you can customize the code generation directly, as you\u2019ll see in the next section.<\/p>\n<p>\u9664\u4e86\u6539\u8fdb OpenAPI \u63cf\u8ff0\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u76f4\u63a5\u81ea\u5b9a\u4e49\u4ee3\u7801\u751f\u6210\uff0c\u5982\u4e0b\u4e00\u8282\u6240\u793a\u3002<\/p>\n<h3>11.4.4 Customizing the generated code<\/h3>\n<h3>11.4.4 \u81ea\u5b9a\u4e49\u751f\u6210\u7684\u4ee3\u7801<\/h3>\n<p>In this section you\u2019ll learn about some of the customization options available with the NSwag generator and why you might want to use them. I look at three customization options in this section:<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3 NSwag \u751f\u6210\u5668\u63d0\u4f9b\u7684\u4e00\u4e9b\u81ea\u5b9a\u4e49\u9009\u9879\u4ee5\u53ca\u60a8\u53ef\u80fd\u5e0c\u671b\u4f7f\u7528\u5b83\u4eec\u7684\u539f\u56e0\u3002\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u67e5\u770b\u4e09\u4e2a\u81ea\u5b9a\u4e49\u9009\u9879\uff1a<\/p>\n<ul>\n<li>\n<p>Using System.Text.Json instead of Newtonsoft.Json for JSON serialization<br \/>\n\u4f7f\u7528 System.Text.Json \u800c\u4e0d\u662f Newtonsoft.Json \u8fdb\u884c JSON \u5e8f\u5217\u5316<\/p>\n<\/li>\n<li>\n<p>Generating an interface for the generated client implementation<br \/>\n\u4e3a\u751f\u6210\u7684\u5ba2\u6237\u7aef\u5b9e\u73b0\u751f\u6210\u63a5\u53e3<\/p>\n<\/li>\n<li>\n<p>Not requiring an explicit BaseAddress parameter in the constructor<br \/>\n\u5728\u6784\u9020\u51fd\u6570\u4e2d\u4e0d\u9700\u8981\u663e\u5f0f BaseAddress \u53c2\u6570<\/p>\n<\/li>\n<\/ul>\n<p>By default, NSwag uses Newtonsoft.Json to serialize requests and deserialize responses. Newtonsoft.Json is a popular, battle-hardened JSON library, but .NET 7 has a built-in JSON library, System.Text.Json, that ASP.NET Core uses by default for JSON serialization. Instead of using two JSON libraries, you may want to replace the serialization used in your client to use System.Text.Json.<\/p>\n<p>\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cNSwag \u4f7f\u7528 Newtonsoft.Json \u6765\u5e8f\u5217\u5316\u8bf7\u6c42\u548c\u53cd\u5e8f\u5217\u5316\u54cd\u5e94\u3002Newtonsoft.Json \u662f\u4e00\u4e2a\u5e38\u7528\u7684\u3001\u4e45\u7ecf\u8003\u9a8c\u7684 JSON \u5e93\uff0c\u4f46 .NET 7 \u6709\u4e00\u4e2a\u5185\u7f6e\u7684 JSON \u5e93 System.Text.Json\uff0cASP.NET Core \u9ed8\u8ba4\u4f7f\u7528\u8be5\u5e93\u8fdb\u884c JSON \u5e8f\u5217\u5316\u3002\u60a8\u53ef\u80fd\u5e0c\u671b\u5c06\u5ba2\u6237\u7aef\u4e2d\u4f7f\u7528\u7684\u5e8f\u5217\u5316\u66ff\u6362\u4e3a System.Text.Json\uff0c\u800c\u4e0d\u662f\u4f7f\u7528\u4e24\u4e2a JSON \u5e93\u3002<\/p>\n<p>When NSwag generates a client, it marks the class as partial, which means that you can define your own partial class FruitClient (for example) and add any methods that you think are useful to the client. The generated client also provides partial methods that act as hooks just before a request is sent or received.<\/p>\n<p>\u5f53 NSwag \u751f\u6210\u5ba2\u6237\u7aef\u65f6\uff0c\u5b83\u4f1a\u5c06\u7c7b\u6807\u8bb0\u4e3a partial\uff0c\u8fd9\u610f\u5473\u7740\u60a8\u53ef\u4ee5\u5b9a\u4e49\u81ea\u5df1\u7684\u90e8\u5206\u7c7b FruitClient\uff08\u4f8b\u5982\uff09\u5e76\u6dfb\u52a0\u60a8\u8ba4\u4e3a\u5bf9\u5ba2\u6237\u7aef\u6709\u7528\u7684\u4efb\u4f55\u65b9\u6cd5\u3002\u751f\u6210\u7684 Client \u7aef\u8fd8\u63d0\u4f9b\u4e86\u90e8\u5206\u65b9\u6cd5\uff0c\u8fd9\u4e9b\u65b9\u6cd5\u5728\u53d1\u9001\u6216\u63a5\u6536\u8bf7\u6c42\u4e4b\u524d\u5145\u5f53 hook\u3002<\/p>\n<p><strong>Definition<\/strong> Partial methods in C# (<a href=\"http:\/\/mng.bz\/zXEB\">http:\/\/mng.bz\/zXEB<\/a>) are void-returning methods that don\u2019t have an implementation. You can define the implementation of the method in a separate partial class file. If you don\u2019t define the implementation, the method is removed at compile time, so you use partial methods as highly performant event handlers.<br \/>\n<strong>\u5b9a\u4e49<\/strong> C# \uff08<a href=\"http:\/\/mng.bz\/zXEB\">http:\/\/mng.bz\/zXEB<\/a>\uff09 \u4e2d\u7684\u5206\u90e8\u65b9\u6cd5\u662f\u6ca1\u6709\u5b9e\u73b0\u7684\u8fd4\u56de void \u7684\u65b9\u6cd5\u3002\u60a8\u53ef\u4ee5\u5728\u5355\u72ec\u7684Partial \u7c7b\u6587\u4ef6\u3002\u5982\u679c\u672a\u5b9a\u4e49\u5b9e\u73b0\uff0c\u5219\u4f1a\u5728\u7f16\u8bd1\u65f6\u5220\u9664\u8be5\u65b9\u6cd5\uff0c\u56e0\u6b64\u5c06\u5206\u90e8\u65b9\u6cd5\u7528\u4f5c\u9ad8\u6027\u80fd\u4e8b\u4ef6\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>Extending your generated clients is useful, but during testing it\u2019s common to also want to substitute your generated client by using interfaces. Interfaces let you substitute fake or mock versions of a service so that your tests aren\u2019t calling the API for real, as you learned in chapter 8. NSwag can help with this process by automatically generating an IFruitClient interface that the FruitClient implements.<\/p>\n<p>\u6269\u5c55\u751f\u6210\u7684 Client \u7aef\u5f88\u6709\u7528\uff0c\u4f46\u5728\u6d4b\u8bd5\u671f\u95f4\uff0c\u901a\u5e38\u8fd8\u5e0c\u671b\u4f7f\u7528 interfaces \u66ff\u6362\u751f\u6210\u7684 Client \u7aef\u3002\u63a5\u53e3\u5141\u8bb8\u4f60\u66ff\u6362\u670d\u52a1\u7684 fake \u6216 mock \u7248\u672c\uff0c\u8fd9\u6837\u4f60\u7684\u6d4b\u8bd5\u5c31\u4e0d\u4f1a\u771f\u6b63\u8c03\u7528 API\uff0c\u6b63\u5982\u4f60\u5728\u7b2c 8 \u7ae0\u4e2d\u5b66\u5230\u7684\u90a3\u6837\u3002NSwag \u53ef\u4ee5\u901a\u8fc7\u81ea\u52a8\u751f\u6210 FruitClient \u5b9e\u73b0\u7684 IFruitClient \u63a5\u53e3\u6765\u5e2e\u52a9\u5b8c\u6210\u6b64\u8fc7\u7a0b\u3002<\/p>\n<p>Finally, providing a base address where the API is hosted makes sense on the face of it. But as we discussed in chapter 9, primitive constructor arguments such as string and int don\u2019t play well with dependency injection. Given that HttpClient contains a BaseAddress property, you can configure NSwag to not require that the base address be passed as a constructor argument and instead set it on the HttpClient type directly. This approach helps in dependency injection (DI) scenarios, as you\u2019ll when we discuss IHttpClientFactory in chapter 33.<\/p>\n<p>\u6700\u540e\uff0c\u63d0\u4f9b\u6258\u7ba1 API \u7684\u57fa\u5740\u4ece\u8868\u9762\u4e0a\u770b\u662f\u6709\u610f\u4e49\u7684\u3002\u4f46\u662f\u6b63\u5982\u6211\u4eec\u5728\u7b2c 9 \u7ae0\u4e2d\u8ba8\u8bba\u7684\u90a3\u6837\uff0c\u50cf string \u548c int \u8fd9\u6837\u7684\u539f\u59cb\u6784\u9020\u51fd\u6570\u53c2\u6570\u4e0d\u80fd\u5f88\u597d\u5730\u4e0e\u4f9d\u8d56\u6ce8\u5165\u914d\u5408\u3002\u9274\u4e8e HttpClient \u5305\u542b BaseAddress \u5c5e\u6027\uff0c\u60a8\u53ef\u4ee5\u5c06 NSwag \u914d\u7f6e\u4e3a\u4e0d\u8981\u6c42\u5c06\u57fa\u5740\u4f5c\u4e3a\u6784\u9020\u51fd\u6570\u53c2\u6570\u4f20\u9012\uff0c\u800c\u662f\u76f4\u63a5\u5728 HttpClient \u7c7b\u578b\u4e0a\u8bbe\u7f6e\u5b83\u3002\u200c\u8fd9\u79cd\u65b9\u6cd5\u5728\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165 \uff08DI\uff09 \u573a\u666f\u4e2d\u6709\u6240\u5e2e\u52a9\uff0c\u6b63\u5982\u6211\u4eec\u5728\u7b2c 33 \u7ae0\u8ba8\u8bba IHttpClientFactory \u65f6\u6240\u770b\u5230\u7684\u90a3\u6837\u3002<\/p>\n<p>These three seemingly unrelated options are all configured in NSwag in the same way: by adding an Options element to the <code>&lt;OpenApiReference&gt;<\/code> element in your project file. The options are provided as command-line switches and must be provided on one line, without line breaks. The switches for the three settings described are<\/p>\n<p>\u8fd9\u4e09\u4e2a\u770b\u4f3c\u65e0\u5173\u7684\u9009\u9879\u5728 NSwag \u4e2d\u90fd\u4ee5\u76f8\u540c\u7684\u65b9\u5f0f\u914d\u7f6e\uff1a\u901a\u8fc7\u5728<code>&lt;OpenApiReference&gt;<\/code> \u5143\u7d20\u3002\u8fd9\u4e9b\u9009\u9879\u4ee5\u547d\u4ee4\u884c\u5f00\u5173\u7684\u5f62\u5f0f\u63d0\u4f9b\uff0c\u5e76\u4e14\u5fc5\u987b\u5728\u4e00\u884c\u4e2d\u63d0\u4f9b\uff0c\u6ca1\u6709\u6362\u884c\u7b26\u3002\u7528\u4e8e\u63cf\u8ff0\u7684\u4e09\u79cd\u8bbe\u7f6e\u7684\u5f00\u5173\u662f<\/p>\n<ul>\n<li>\n<p>\/UseBaseUrl:false\u2014When false, NSwag removes the baseUrl parameter from the generated client\u2019s constructor and instead relies on HttpClient to have the correct base address. It defaults to true.<br \/>\n\/UseBaseUrl\uff1afalse \u2014 \u5f53 false \u65f6\uff0cNSwag \u4ece\u751f\u6210\u7684\u5ba2\u6237\u7aef\u7684\u6784\u9020\u51fd\u6570\u4e2d\u5220\u9664 baseUrl \u53c2\u6570\uff0c\u800c\u662f\u4f9d\u8d56 HttpClient \u5177\u6709\u6b63\u786e\u7684\u57fa\u5740\u3002\u5b83\u9ed8\u8ba4\u4e3a true\u3002<\/p>\n<\/li>\n<li>\n<p>\/GenerateClientInterfaces:true\u2014When true, NSwag generates an interface for the client, containing all the endpoints. The generated client implements this interface. It defaults to false.<br \/>\n\/GenerateClientInterfaces\uff1atrue \u2014 \u5982\u679c\u4e3a true\uff0c\u5219 NSwag \u4f1a\u4e3a\u5ba2\u6237\u7aef\u751f\u6210\u4e00\u4e2a\u63a5\u53e3\uff0c\u5176\u4e2d\u5305\u542b\u6240\u6709\u7aef\u70b9\u3002\u751f\u6210\u7684 Client \u7aef\u5b9e\u73b0\u6b64\u63a5\u53e3\u3002\u5b83\u9ed8\u8ba4\u4e3a false\u3002<\/p>\n<\/li>\n<li>\n<p>\/JsonLibrary:SystemTextJson\u2014This switch specifies the JSON serialization library to use. It defaults to using Newtonsoft.Json.<br \/>\n\/JsonLibrary\uff1aSystemTextJson - \u6b64\u5f00\u5173\u6307\u5b9a\u8981\u4f7f\u7528\u7684 JSON \u5e8f\u5217\u5316\u5e93\u3002\u5b83\u9ed8\u8ba4\u4f7f\u7528 Newtonsoft.Json\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>Tip<\/strong> A vast number of configuration options is available for NSwag. I find that the best documentation is available in the NSwag .NET tool. You can install the tool by using dotnet tool install -g NSwag.ConsoleCore, and you can view the available options by running nswag help openapi2csclient.<br \/>\n<strong>\u63d0\u793a<\/strong> NSwag \u6709\u5927\u91cf\u7684\u914d\u7f6e\u9009\u9879\u53ef\u7528\u3002\u6211\u53d1\u73b0 NSwag .NET \u5de5\u5177\u4e2d\u63d0\u4f9b\u4e86\u6700\u597d\u7684\u6587\u6863\u3002\u53ef\u4ee5\u4f7f\u7528 dotnet tool install -g NSwag.ConsoleCore \u5b89\u88c5\u8be5\u5de5\u5177\uff0c\u5e76\u4e14\u53ef\u4ee5\u901a\u8fc7\u8fd0\u884c nswag help openapi2csclient \u6765\u67e5\u770b\u53ef\u7528\u9009\u9879\u3002<\/p>\n<p>You can set all three of these options by adding an <code>&lt;Options&gt;<\/code> element to the <code>&lt;OpenApiReference&gt;<\/code> element, as shown in the following listing. Make sure that you open and close both elements correctly so the XML stays valid; it\u2019s an easy mistake to make when editing by hand!<\/p>\n<p>\u60a8\u53ef\u4ee5\u901a\u8fc7\u5411 <code>&lt;&lt;OpenApiReference&gt;<\/code> \u5143\u7d20\u6dfb\u52a0 <code>&lt;Options&gt;<\/code> \u5143\u7d20\u6765\u8bbe\u7f6e\u6240\u6709\u8fd9\u4e09\u4e2a\u9009\u9879\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002\u786e\u4fdd\u6b63\u786e\u6253\u5f00\u548c\u5173\u95ed\u8fd9\u4e24\u4e2a\u5143\u7d20\uff0c\u4ee5\u4fbf XML \u4fdd\u6301\u6709\u6548;\u624b\u52a8\u7f16\u8f91\u65f6\u5f88\u5bb9\u6613\u72af\u9519\u8bef\uff01\u200c<\/p>\n<p>Listing 11.7 Customizing NSwag generator options<br \/>\n\u6e05\u5355 11.7 \u81ea\u5b9a\u4e49 NSwag \u751f\u6210\u5668\u9009\u9879<\/p>\n<pre><code>&lt;OpenApiReference Include=&quot;OpenAPIs\\fruit.json&quot;\n  SourceUrl=&quot;http:\/\/localhost:5062\/swagger\/v1\/swagger.json&quot;\n  Namespace=&quot;Fruit&quot;\n  ClassName=&quot;FruitClient&quot; &gt;\n    &lt;Options&gt;\/UseBaseUrl:false \/GenerateClientInterfaces:true \n[CA]\/JsonLibrary:SystemTextJson&lt;\/Options&gt;  \u2776\n&lt;\/OpenApiReference&gt;  \u2777<\/code><\/pre>\n<p>\u2776 Customizes the options NSwag uses for code generation<br \/>\n\u81ea\u5b9a\u4e49 NSwag \u7528\u4e8e\u4ee3\u7801\u751f\u6210\u7684\u9009\u9879<br \/>\n\u2777 Make sure to close the outer XML element to keep the XML valid.<br \/>\n\u786e\u4fdd\u5173\u95ed\u5916\u90e8 XML \u5143\u7d20\u4ee5\u4fdd\u6301 XML \u6709\u6548\u3002<\/p>\n<p>You\u2019d be forgiven for thinking that after making these changes, NSwag would update the generated code next time you build. Unfortunately, it\u2019s not necessarily that simple. NSwag watches for changes to the OpenAPI description JSON file saved in your project and will regenerate the code any time the file changes, but it won\u2019t necessarily update when you change options in your csproj file. Even worse, doing a clean or rebuild similarly has no effect. If you find yourself in this situation, it\u2019s best to delete the obj folder for your project to ensure that everything regenerates correctly.<\/p>\n<p>\u5982\u679c\u4f60\u8ba4\u4e3a\u5728\u8fdb\u884c\u8fd9\u4e9b\u66f4\u6539\u540e\uff0cNSwag \u4f1a\u5728\u4f60\u4e0b\u6b21\u6784\u5efa\u65f6\u66f4\u65b0\u751f\u6210\u7684\u4ee3\u7801\uff0c\u8fd9\u662f\u53ef\u4ee5\u7406\u89e3\u7684\u3002\u4e0d\u5e78\u7684\u662f\uff0c\u4e8b\u60c5\u4e0d\u4e00\u5b9a\u90a3\u4e48\u7b80\u5355\u3002NSwag \u4f1a\u76d1\u89c6\u9879\u76ee\u4e2d\u4fdd\u5b58\u7684 OpenAPI \u63cf\u8ff0 JSON \u6587\u4ef6\u7684\u66f4\u6539\uff0c\u5e76\u5728\u6587\u4ef6\u66f4\u6539\u65f6\u91cd\u65b0\u751f\u6210\u4ee3\u7801\uff0c\u4f46\u5f53\u60a8\u66f4\u6539 csproj \u6587\u4ef6\u4e2d\u7684\u9009\u9879\u65f6\uff0c\u5b83\u4e0d\u4e00\u5b9a\u4f1a\u66f4\u65b0\u3002\u66f4\u7cdf\u7cd5\u7684\u662f\uff0c\u4ee5\u7c7b\u4f3c\u7684\u65b9\u5f0f\u8fdb\u884c\u6e05\u7406\u6216\u91cd\u5efa\u4e5f\u6ca1\u6709\u6548\u679c\u3002\u5982\u679c\u60a8\u53d1\u73b0\u81ea\u5df1\u5904\u4e8e\u8fd9\u79cd\u60c5\u51b5\uff0c\u6700\u597d\u5220\u9664\u9879\u76ee\u7684 obj \u6587\u4ef6\u5939\uff0c\u4ee5\u786e\u4fdd\u6240\u6709\u5185\u5bb9\u90fd\u80fd\u6b63\u786e\u91cd\u65b0\u751f\u6210\u3002<\/p>\n<p><strong>Tip<\/strong> Another option is to make a tiny change in the OpenAPI document so that NSwag updates the generated code when you build your project. Then you can revert the OpenAPI document change.<br \/>\n<strong>\u63d0\u793a<\/strong> \u53e6\u4e00\u79cd\u9009\u62e9\u662f\u5bf9 OpenAPI \u6587\u6863\u8fdb\u884c\u5fae\u5c0f\u7684\u66f4\u6539\uff0c\u4ee5\u4fbf NSwag \u5728\u60a8\u6784\u5efa\u9879\u76ee\u65f6\u66f4\u65b0\u751f\u6210\u7684\u4ee3\u7801\u3002\u7136\u540e\uff0c\u60a8\u53ef\u4ee5\u8fd8\u539f OpenAPI \u6587\u6863\u66f4\u6539\u3002<\/p>\n<p>After you\u2019ve persuaded NSwag to regenerate the client, you should update your code to use the new features. You can remove the Newtonsoft.Json reference from your csproj file and update your Program.cs as shown in the following listing.<\/p>\n<p>\u5728\u4f60\u8bf4\u670d NSwag \u91cd\u65b0\u751f\u6210\u5ba2\u6237\u7aef\u4e4b\u540e\uff0c\u4f60\u5e94\u8be5\u66f4\u65b0\u4f60\u7684\u4ee3\u7801\u4ee5\u4f7f\u7528\u65b0\u529f\u80fd\u3002\u60a8\u53ef\u4ee5\u4ece csproj \u6587\u4ef6\u4e2d\u5220\u9664 Newtonsoft.Json \u5f15\u7528\u5e76\u66f4\u65b0Program.cs\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 11.8 Using the updated NSwag client<br \/>\n\u6e05\u5355 11.8 \u4f7f\u7528\u66f4\u65b0\u7684 NSwag \u5ba2\u6237\u7aef<\/p>\n<pre><code>using Fruit;\n\nIFruitClient client = new FruitClient(    \u2776\n    new HttpClient() { BaseAddress =     \u2777\n        new Uri(&quot;https:\/\/localhost:7186&quot;) });    \u2777\n\nFruit.Fruit created = await client.FruitPOSTAsync(&quot;123&quot;,\n    new Fruit.Fruit { Name = &quot;Banana&quot;, Stock = 100 });\nConsole.WriteLine($&quot;Created {created.Name}&quot;);\n\nFruit.Fruit fetched = await client.FruitGETAsync(&quot;123&quot;);\nConsole.WriteLine($&quot;Fetched {fetched.Name}&quot;);<\/code><\/pre>\n<p>\u2776 FruitClient now implements IFruitClient.<br \/>\nFruitClient \u73b0\u5728\u5b9e\u73b0 IFruitClient\u3002<br \/>\n\u2777 Sets the base address on HttpClient instead of passing as a constructor argument<br \/>\n\u5728 HttpClient \u4e0a\u8bbe\u7f6e\u57fa\u5740\uff0c\u800c\u4e0d\u662f\u4f5c\u4e3a\u6784\u9020\u51fd\u6570\u53c2\u6570\u4f20\u9012<\/p>\n<p>If you updated the operation IDs for your API endpoints using WithName(), you may be a little surprised to see that you still have the ugly FruitPOSTAsync and FruitGETAsync methods, even though you regenerated the client. That\u2019s because the OpenAPI description saved to your project is downloaded only once, when you initially add it. Let\u2019s look at how to update the local OpenAPI document to reflect the changes to your remote API.<\/p>\n<p>\u5982\u679c\u60a8\u4f7f\u7528 WithName\uff08\uff09 \u66f4\u65b0\u4e86 API \u7ec8\u7aef\u8282\u70b9\u7684\u4f5c ID\uff0c\u60a8\u53ef\u80fd\u4f1a\u6709\u70b9\u60ca\u8bb6\u5730\u53d1\u73b0\uff0c\u5373\u4f7f\u60a8\u91cd\u65b0\u751f\u6210\u4e86\u5ba2\u6237\u7aef\uff0c\u60a8\u4ecd\u7136\u62e5\u6709\u4e11\u964b\u7684 FruitPOSTAsync \u548c FruitGETAsync \u65b9\u6cd5\u3002\u8fd9\u662f\u56e0\u4e3a\u4fdd\u5b58\u5230\u9879\u76ee\u7684 OpenAPI \u63cf\u8ff0\u5728\u60a8\u6700\u521d\u6dfb\u52a0\u65f6\u4ec5\u4e0b\u8f7d\u4e00\u6b21\u3002\u8ba9\u6211\u4eec\u770b\u770b\u5982\u4f55\u66f4\u65b0\u672c\u5730 OpenAPI \u6587\u6863\u4ee5\u53cd\u6620\u5bf9\u8fdc\u7a0b API \u7684\u66f4\u6539\u3002<\/p>\n<h3>11.4.5 Refreshing the OpenAPI description<\/h3>\n<h3>11.4.5 \u5237\u65b0 OpenAPI \u63cf\u8ff0<\/h3>\n<p>In this section you\u2019ll learn how to update the OpenAPI description document saved to your project that\u2019s used for generation. This document doesn\u2019t update automatically, so the client generated by NSwag may not reflect the latest OpenAPI description for your API.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u66f4\u65b0\u4fdd\u5b58\u5230\u9879\u76ee\u4e2d\u7528\u4e8e\u751f\u6210\u7684 OpenAPI \u63cf\u8ff0\u6587\u6863\u3002\u672c\u6587\u6863\u4e0d\u4f1a\u81ea\u52a8\u66f4\u65b0\uff0c\u56e0\u6b64 NSwag \u751f\u6210\u7684\u5ba2\u6237\u7aef\u53ef\u80fd\u65e0\u6cd5\u53cd\u6620\u60a8\u7684 API \u7684\u6700\u65b0 OpenAPI \u63cf\u8ff0\u3002<\/p>\n<p>Whether you used Visual Studio (as in section 11.4.1) or the .NET OpenAPI tool (as in section 11.4.2), the OpenAPI description saved as a JSON file to your project is a point-in-time snapshot of the API. If you add more metadata to your API, you need to download the OpenAPI description to your project again.<\/p>\n<p>\u65e0\u8bba\u60a8\u4f7f\u7528\u7684\u662f Visual Studio\uff08\u5982\u7b2c 11.4.1 \u8282\u6240\u793a\uff09\u8fd8\u662f .NET OpenAPI \u5de5\u5177\uff08\u5982\u7b2c 11.4.2 \u8282\u6240\u793a\uff09\uff0c\u4ee5 JSON \u6587\u4ef6\u5f62\u5f0f\u4fdd\u5b58\u5230\u9879\u76ee\u4e2d\u7684 OpenAPI \u63cf\u8ff0\u90fd\u662f API \u7684\u65f6\u95f4\u70b9\u5feb\u7167\u3002\u5982\u679c\u60a8\u5411 API \u6dfb\u52a0\u66f4\u591a\u5143\u6570\u636e\uff0c\u5219\u9700\u8981\u518d\u6b21\u5c06 OpenAPI \u63cf\u8ff0\u4e0b\u8f7d\u5230\u60a8\u7684\u9879\u76ee\u4e2d\u3002<\/p>\n<p><strong>Tip<\/strong> My preferred approach is low-tech: I simply navigate to the OpenAPI description in the browser, copy the JSON contents, and paste it into the JSON file in my project.<br \/>\n<strong>\u63d0\u793a<\/strong> \u6211\u7684\u9996\u9009\u65b9\u6cd5\u662f\u4f4e\u6280\u672f\u542b\u91cf\u7684\u65b9\u6cd5\uff1a\u6211\u53ea\u9700\u5728\u6d4f\u89c8\u5668\u4e2d\u5bfc\u822a\u5230 OpenAPI \u63cf\u8ff0\uff0c\u590d\u5236 JSON \u5185\u5bb9\uff0c\u7136\u540e\u5c06\u5176\u7c98\u8d34\u5230\u6211\u9879\u76ee\u7684 JSON \u6587\u4ef6\u4e2d\u3002<\/p>\n<p>If you don\u2019t want to update the OpenAPI description manually, you can use Visual Studio or the .NET OpenAPI tool to refresh the saved document for you.<\/p>\n<p>\u5982\u679c\u4e0d\u60f3\u624b\u52a8\u66f4\u65b0 OpenAPI \u8bf4\u660e\uff0c\u53ef\u4ee5\u4f7f\u7528 Visual Studio \u6216 .NET OpenAPI \u5de5\u5177\u5237\u65b0\u4e3a\u60a8\u4fdd\u5b58\u6587\u6863\u3002<\/p>\n<p><strong>Warning<\/strong> If you originally used Visual Studio, you can\u2019t refresh the document by using the OpenAPI tool and vice versa. The reason is that Visual Studio uses the SourceUri attribute on the OpenApiReference element and the .NET tool uses the SourceUrl attribute. And yes, that situation is arbitrary and annoying!<br \/>\n<strong>\u8b66\u544a<\/strong> \u5982\u679c\u60a8\u6700\u521d\u4f7f\u7528\u7684\u662f Visual Studio\uff0c\u5219\u65e0\u6cd5\u4f7f\u7528 OpenAPI \u5de5\u5177\u5237\u65b0\u6587\u6863\uff0c\u53cd\u4e4b\u4ea6\u7136\u3002\u539f\u56e0\u662f Visual Studio \u4f7f\u7528 OpenApiReference \u5143\u7d20\u4e0a\u7684 SourceUri \u5c5e\u6027\uff0c\u800c .NET \u5de5\u5177\u4f7f\u7528 SourceUrl \u5c5e\u6027\u3002\u662f\u7684\uff0c\u8fd9\u79cd\u60c5\u51b5\u662f\u6b66\u65ad\u548c\u70e6\u4eba\u7684\uff01<\/p>\n<p>To update your OpenAPI description by using Visual Studio, follow these steps:<br \/>\n\u8981\u4f7f\u7528 Visual Studio \u66f4\u65b0 OpenAPI \u63cf\u8ff0\uff0c\u8bf7\u6267\u884c\u4ee5\u4e0b\u6b65\u9aa4\uff1a<\/p>\n<ol>\n<li>\n<p>Ensure that your API is running and that the OpenAPI description document is available.<br \/>\n\u786e\u4fdd\u60a8\u7684 API \u6b63\u5728\u8fd0\u884c\uff0c\u5e76\u4e14 OpenAPI \u63cf\u8ff0\u6587\u6863\u53ef\u7528\u3002<\/p>\n<\/li>\n<li>\n<p>Navigate to the connected services page for your project by choosing Project &gt; Connected Services &gt; Manage Connected Services.<br \/>\n\u901a\u8fc7\u9009\u62e9 Project &gt; Connected Services \u5bfc\u822a\u5230\u9879\u76ee\u7684 Connected Services \u9875\u9762&gt; \u7ba1\u7406\u8fde\u63a5\u7684\u670d\u52a1\u3002<\/p>\n<\/li>\n<\/ol>\n<p>3.Select the overflow button next to your OpenAPI reference and choose Refresh, as shown in figure 11.9. Then choose Yes in the dialog box to update your OpenAPI document.<br \/>\n\u9009\u62e9 OpenAPI \u5f15\u7528\u65c1\u8fb9\u7684\u6ea2\u51fa\u6309\u94ae\uff0c\u7136\u540e\u9009\u62e9 Refresh\uff0c\u5982\u56fe11.9\u6240\u793a. \u7136\u540e\u5728\u5bf9\u8bdd\u6846\u4e2d\u9009\u62e9 Yes \u4ee5\u66f4\u65b0\u60a8\u7684 OpenAPI \u6587\u6863\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1109.png\" alt=\"alt text \" \/><\/p>\n<p>Figure 11.9 Updating the OpenAPI description for an API. Choose Refresh to download the OpenAPI description again and save it to your project. Then NSwag will generate an updated client on the next build.<br \/>\n\u56fe 11.9 \u66f4\u65b0 API \u7684 OpenAPI \u63cf\u8ff0\u3002\u9009\u62e9 Refresh \uff08\u5237\u65b0\uff09 \u4ee5\u518d\u6b21\u4e0b\u8f7d OpenAPI \u63cf\u8ff0\u5e76\u5c06\u5176\u4fdd\u5b58\u5230\u60a8\u7684\u9879\u76ee\u4e2d\u3002\u7136\u540e NSwag \u5c06\u5728\u4e0b\u4e00\u4e2a\u6784\u5efa\u4e2d\u751f\u6210\u66f4\u65b0\u7684\u5ba2\u6237\u7aef\u3002<\/p>\n<p>To update your OpenAPI description by using the .NET OpenAPI tool, follow these steps:<br \/>\n\u82e5\u8981\u4f7f\u7528 .NET OpenAPI \u5de5\u5177\u66f4\u65b0 OpenAPI \u8bf4\u660e\uff0c\u8bf7\u6267\u884c\u4ee5\u4e0b\u6b65\u9aa4\uff1a<\/p>\n<ol>\n<li>\n<p>Ensure that your API is running and that the OpenAPI description document is available.<br \/>\n\u786e\u4fdd\u60a8\u7684 API \u6b63\u5728\u8fd0\u884c\uff0c\u5e76\u4e14 OpenAPI \u63cf\u8ff0\u6587\u6863\u53ef\u7528\u3002<\/p>\n<\/li>\n<li>\n<p>From your project folder, run the following command, using the same URL you used to add the OpenAPI description originally:<br \/>\n\u5728\u9879\u76ee\u6587\u4ef6\u5939\u4e2d\uff0c\u4f7f\u7528\u6700\u521d\u7528\u4e8e\u6dfb\u52a0 OpenAPI \u63cf\u8ff0\u7684\u76f8\u540c URL \u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n<\/li>\n<\/ol>\n<pre><code>dotnet openapi refresh http:\/\/localhost:5062\/swagger\/v1\/swagger.json<\/code><\/pre>\n<p>After updating your OpenAPI description by using either Visual Studio or the .NET tool, build your application to trigger NSwag to regenerate your client. Any changes you made to your OpenAPI description (such as adding operation IDs) will be reflected in the generated code.<\/p>\n<p>\u4f7f\u7528 Visual Studio \u6216 .NET \u5de5\u5177\u66f4\u65b0 OpenAPI \u63cf\u8ff0\u540e\uff0c\u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u4ee5\u89e6\u53d1 NSwag \u91cd\u65b0\u751f\u6210\u5ba2\u6237\u7aef\u3002\u60a8\u5bf9 OpenAPI \u63cf\u8ff0\u6240\u505a\u7684\u4efb\u4f55\u66f4\u6539\uff08\u4f8b\u5982\u6dfb\u52a0\u4f5c ID\uff09\u90fd\u5c06\u53cd\u6620\u5728\u751f\u6210\u7684\u4ee3\u7801\u4e2d\u3002<\/p>\n<p>I think that client generation is the killer app for OpenAPI descriptions, but it works best when you use metadata to add extensive documentation to your APIs. In section 11.5 you\u2019ll learn how to go one step further by adding summaries and descriptions to your endpoints.<\/p>\n<p>\u6211\u8ba4\u4e3a\u5ba2\u6237\u7aef\u751f\u6210\u662f OpenAPI \u63cf\u8ff0\u7684\u6740\u624b\u7ea7\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f46\u5f53\u60a8\u4f7f\u7528\u5143\u6570\u636e\u5411 API \u6dfb\u52a0\u5927\u91cf\u6587\u6863\u65f6\uff0c\u5b83\u7684\u6548\u679c\u6700\u4f73\u3002\u5728 Section 11.5 \u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u901a\u8fc7\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u6458\u8981\u548c\u63cf\u8ff0\u6765\u66f4\u8fdb\u4e00\u6b65\u3002<\/p>\n<h2>11.5 Adding descriptions and summaries to your endpoints<\/h2>\n<h2>11.5 \u5411\u7ec8\u7aef\u8282\u70b9 \u6dfb\u52a0\u63cf\u8ff0\u548c\u6458\u8981<\/h2>\n<p>In this section you\u2019ll learn how to add extra descriptions and summaries to your OpenAPI description document. Tools such as Swagger UI and NSwag use these extra descriptions and summaries to provide a better developer experience working with your API. You\u2019ll also learn about alternative ways to add metadata to your minimal API endpoints.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5411 OpenAPI \u63cf\u8ff0\u6587\u6863\u6dfb\u52a0\u989d\u5916\u7684\u63cf\u8ff0\u548c\u6458\u8981\u3002Swagger UI \u548c NSwag \u7b49\u5de5\u5177\u4f7f\u7528\u8fd9\u4e9b\u989d\u5916\u7684\u63cf\u8ff0\u548c\u6458\u8981\u6765\u63d0\u4f9b\u66f4\u597d\u7684\u5f00\u53d1\u4eba\u5458\u4f7f\u7528 API \u7684\u4f53\u9a8c\u3002\u60a8\u8fd8\u5c06\u4e86\u89e3\u5c06\u5143\u6570\u636e\u6dfb\u52a0\u5230\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\u7684\u66ff\u4ee3\u65b9\u6cd5\u3002<\/p>\n<h3>11.5.1 Using fluent methods to add descriptions<\/h3>\n<h3>11.5.1 \u4f7f\u7528 Fluent \u65b9\u6cd5\u6dfb\u52a0\u63cf\u8ff0<\/h3>\n<p>Whilst working with your minimal API endpoints and calling methods such as WithName() and WithTags(), you may have noticed the methods WithSummary() and WithDescription(). These methods add metadata to your endpoint in exactly the same way as the other With* methods, but unfortunately, they don\u2019t update your OpenAPI description without some extra changes.<\/p>\n<p>\u5728\u4f7f\u7528\u6700\u5c0f API \u7aef\u70b9\u5e76\u8c03\u7528 WithName\uff08\uff09 \u548c WithTags\uff08\uff09 \u7b49\u65b9\u6cd5\u65f6\uff0c\u60a8\u53ef\u80fd\u5df2\u7ecf\u6ce8\u610f\u5230\u4e86 WithSummary\uff08\uff09 \u548c WithDescription\uff08\uff09 \u65b9\u6cd5\u3002\u8fd9\u4e9b\u65b9\u6cd5\u4ee5\u4e0e\u5176\u4ed6 With* \u65b9\u6cd5\u5b8c\u5168\u76f8\u540c\u7684\u65b9\u5f0f\u5c06\u5143\u6570\u636e\u6dfb\u52a0\u5230\u60a8\u7684\u7ec8\u7aef\u8282\u70b9\uff0c\u4f46\u9057\u61be\u7684\u662f\uff0c\u5982\u679c\u4e0d\u8fdb\u884c\u4e00\u4e9b\u989d\u5916\u66f4\u6539\uff0c\u5b83\u4eec\u4e0d\u4f1a\u66f4\u65b0\u60a8\u7684 OpenAPI \u63cf\u8ff0\u3002<\/p>\n<p>To make use of the summary and description metadata, you must add an extra NuGet package, Microsoft.AspNetCore.OpenApi, and call WithOpenApi() on your endpoint. This method ensures that the summary and description metadata are added correctly to the OpenAPI description when Swashbuckle generates the document. Add this package via the NuGet package manager or the .NET CLI by calling<\/p>\n<p>\u82e5\u8981\u4f7f\u7528\u6458\u8981\u548c\u8bf4\u660e\u5143\u6570\u636e\uff0c\u5fc5\u987b\u6dfb\u52a0\u989d\u5916\u7684 NuGet \u5305 Microsoft.AspNetCore.OpenApi\uff0c\u5e76\u5728\u7ec8\u7ed3\u70b9\u4e0a\u8c03\u7528 WithOpenApi\uff08\uff09\u3002\u6b64\u65b9\u6cd5\u53ef\u786e\u4fdd\u5728 Swashbuckle \u751f\u6210\u6587\u6863\u65f6\u5c06\u6458\u8981\u548c\u63cf\u8ff0\u5143\u6570\u636e\u6b63\u786e\u6dfb\u52a0\u5230 OpenAPI \u63cf\u8ff0\u4e2d\u3002\u901a\u8fc7 NuGet \u5305\u7ba1\u7406\u5668\u6216 .NET CLI \u6dfb\u52a0\u6b64\u5305<\/p>\n<pre><code>dotnet add package Microsoft.AspNetCore.OpenApi<\/code><\/pre>\n<p>from the project folder. Then update your endpoints to add summaries and\/or descriptions, making sure to call WithOpenApi(), as shown in the following listing.<\/p>\n<p>\u4ece\u9879\u76ee\u6587\u4ef6\u5939\u4e2d\u3002\u7136\u540e\u66f4\u65b0\u60a8\u7684\u7ec8\u7aef\u8282\u70b9\u4ee5\u6dfb\u52a0\u6458\u8981\u548c\/\u6216\u63cf\u8ff0\uff0c\u786e\u4fdd\u8c03\u7528 WithOpenApi\uff08\uff09\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 11.9 Adding summaries and descriptions to endpoints using WithOpenApi()<br \/>\n\u5217\u8868 11.9 \u4f7f\u7528 WithOpenApi\uff08\uff09 \u5411\u7aef\u70b9\u6dfb\u52a0\u6458\u8981\u548c\u63cf\u8ff0<\/p>\n<pre><code>using System.Collections.Concurrent;\n\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nWebApplication app = builder.Build();\n\napp.UseSwagger();\napp.UseSwaggerUI();\n\nvar _fruit = new ConcurrentDictionary&lt;string, Fruit&gt;();\n\napp.MapGet(&quot;\/fruit\/{id}&quot;, (string id) =&gt;\n    _fruit.TryGetValue(id, out var fruit)\n        ? TypedResults.Ok(fruit)\n        : Results.Problem(statusCode: 404))\n    .WithName(&quot;GetFruit&quot;)\n    .WithTags(&quot;fruit&quot;)\n    .Produces&lt;Fruit&gt;()\n    .ProducesProblem(404)\n    .WithSummary(&quot;Fetches a fruit&quot;)    \u2776\n    .WithDescription(&quot;Fetches a fruit by id, or returns 404&quot; +    \u2777\n      &quot; if no fruit with the ID exists&quot;)    \u2777\n    .WithOpenApi();    \u2778\n\napp.Run();\nrecord Fruit(string Name, int Stock);\n<\/code><\/pre>\n<p>\u2776 Adds a summary to the endpoint<br \/>\n\u5411\u7aef\u70b9\u6dfb\u52a0\u6458\u8981<br \/>\n\u2777 Adds a description to the endpoint<br \/>\n\u5411\u7aef\u70b9\u6dfb\u52a0\u63cf\u8ff0<br \/>\n\u2778 Exposes the metadata added by summary and description to the OpenAPI description<br \/>\n\u5c06 summary \u548c description \u6dfb\u52a0\u7684\u5143\u6570\u636e\u516c\u5f00\u5230 OpenAPI \u63cf\u8ff0<\/p>\n<p>With these changes, Swagger UI reflects the extra metadata, as shown in figure 11.10. NSwag also uses the summary as a documentation comment when it generates the endpoints on the client. You can see in figure 11.10, however, that one piece of documentation is missing: a description of the parameter id.<br \/>\n\u901a\u8fc7\u8fd9\u4e9b\u66f4\u6539\uff0cSwagger UI \u4f1a\u53cd\u6620\u989d\u5916\u7684\u5143\u6570\u636e\uff0c\u5982\u56fe 11.10 \u6240\u793a\u3002NSwag \u5728\u5ba2\u6237\u7aef\u4e0a\u751f\u6210\u7aef\u70b9\u65f6\uff0c\u8fd8\u4f1a\u5c06\u6458\u8981\u7528\u4f5c\u6587\u6863\u6ce8\u91ca\u3002\u4f46\u662f\uff0c\u60a8\u53ef\u4ee5\u5728\u56fe 11.10 \u4e2d\u770b\u5230\u7f3a\u5c11\u4e00\u6761\u6587\u6863\uff1a\u53c2\u6570 id \u7684\u63cf\u8ff0\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1110.png\" alt=\"alt text \" \/><\/p>\n<p>Figure 11.10 The summary and description metadata displayed in the Swagger UI. Note that no description is displayed for the id parameter.<br \/>\n\u56fe 11.10 Swagger UI \u4e2d\u663e\u793a\u7684\u6458\u8981\u548c\u63cf\u8ff0\u5143\u6570\u636e\u3002\u8bf7\u6ce8\u610f\uff0c\u4e0d\u663e\u793a id \u53c2\u6570\u7684\u63cf\u8ff0\u3002<\/p>\n<p>Unfortunately, you don\u2019t have a particularly elegant way to add documentation for your parameters. The suggested approach is to use an overload of the WithOpenApi() method, which takes a lambda method where you can add a description for the parameter:<br \/>\n\u9057\u61be\u7684\u662f\uff0c\u60a8\u6ca1\u6709\u4e00\u79cd\u7279\u522b\u4f18\u96c5\u7684\u65b9\u6cd5\u6765\u4e3a\u60a8\u7684\u53c2\u6570\u6dfb\u52a0\u6587\u6863\u3002\u5efa\u8bae\u7684\u65b9\u6cd5\u662f\u4f7f\u7528 WithOpenApi\uff08\uff09 \u65b9\u6cd5\u7684\u91cd\u8f7d\uff0c\u8be5\u65b9\u6cd5\u91c7\u7528 lambda \u65b9\u6cd5\uff0c\u60a8\u53ef\u4ee5\u5728\u5176\u4e2d\u6dfb\u52a0\u53c2\u6570\u7684\u63cf\u8ff0\uff1a<\/p>\n<pre><code>.WithOpenApi(o =&gt;\n{\n    o.Parameters[0].Description = &quot;The id of the fruit to fetch&quot;;\n    o.Summary = &quot;Fetches a fruit&quot;;\n    return o;\n});<\/code><\/pre>\n<p>This example shows that you can use the WithOpenApi() method to set any of the OpenAPI metadata for the endpoint, so you can use this single method to set (for example) the summary and tags instead of using the dedicated WithSummary() or WithTags() method.<\/p>\n<p>\u6b64\u793a\u4f8b\u663e\u793a\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 WithOpenApi\uff08\uff09 \u65b9\u6cd5\u4e3a\u7ec8\u7aef\u8282\u70b9\u8bbe\u7f6e\u4efb\u4f55 OpenAPI \u5143\u6570\u636e\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u4f7f\u7528\u6b64\u5355\u4e00\u65b9\u6cd5\u6765\u8bbe\u7f6e\uff08\u4f8b\u5982\uff09\u6458\u8981\u548c\u6807\u7b7e\uff0c\u800c\u4e0d\u662f\u4f7f\u7528\u4e13\u7528\u7684 WithSummary\uff08\uff09 \u6216 WithTags\uff08\uff09 \u65b9\u6cd5\u3002<\/p>\n<p>Adding all this metadata undoubtedly documents your API in more detail and makes your generated code easier to understand. But if you\u2019re anything like me, the sheer number of methods you have to call makes it hard to see where your endpoint ends and the metadata begins! In section 11.5.2 we\u2019ll look at an alternative approach that involves using attributes.<\/p>\n<p>\u6dfb\u52a0\u6240\u6709\u8fd9\u4e9b\u5143\u6570\u636e\u65e0\u7591\u53ef\u4ee5\u66f4\u8be6\u7ec6\u5730\u8bb0\u5f55\u60a8\u7684 API\uff0c\u5e76\u4f7f\u60a8\u751f\u6210\u7684\u4ee3\u7801\u66f4\u6613\u4e8e\u7406\u89e3\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u548c\u6211\u4e00\u6837\uff0c\u60a8\u5fc5\u987b\u8c03\u7528\u7684\u65b9\u6cd5\u6570\u91cf\u4e4b\u591a\u4f7f\u5f97\u5f88\u96be\u770b\u5230\u60a8\u7684\u7ec8\u7aef\u8282\u70b9\u4ece\u4f55\u5904\u7ed3\u675f\uff0c\u5143\u6570\u636e\u4ece\u4f55\u5904\u5f00\u59cb\uff01\u5728 Section 11.5.2 \u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u4e00\u79cd\u6d89\u53ca\u4f7f\u7528 attributes \u7684\u66ff\u4ee3\u65b9\u6cd5\u3002<\/p>\n<h3>11.5.2 Using attributes to add metadata<\/h3>\n<h3>11.5.2 \u4f7f\u7528\u5c5e\u6027\u6dfb\u52a0\u5143\u6570\u636e<\/h3>\n<p>I\u2019m a fan of fluent interfaces in many cases, as I feel that they make code easier to understand. But the endpoint metadata extensions, such as those shown in listing 11.9, go to extremes. It\u2019s hard to understand what the endpoint is doing with all the noise from the metadata methods! Ever since version 1.0, C# has had a canonical way to add metadata to code\u2014attributes\u2014and you can replace your endpoint extension methods with dedicated attributes if you prefer.<\/p>\n<p>\u5728\u8bb8\u591a\u60c5\u51b5\u4e0b\uff0c\u6211\u662f Fluent \u63a5\u53e3\u7684\u7c89\u4e1d\uff0c\u56e0\u4e3a\u6211\u89c9\u5f97\u5b83\u4eec\u4f7f\u4ee3\u7801\u66f4\u5bb9\u6613\u7406\u89e3\u3002\u4f46\u662f\u7aef\u70b9\u5143\u6570\u636e\u6269\u5c55\uff08\u5982\u6e05\u5355 11.9 \u4e2d\u6240\u793a\u7684\u6269\u5c55\uff09\u8d70\u5411\u4e86\u6781\u7aef\u3002\u5f88\u96be\u7406\u89e3\u7ec8\u7aef\u8282\u70b9\u5bf9\u5143\u6570\u636e\u65b9\u6cd5\u7684\u6240\u6709\u5e72\u6270\u505a\u4e86\u4ec0\u4e48\uff01\u4ece 1.0 \u7248\u5f00\u59cb\uff0cC# \u5c31\u6709\u4e00\u79cd\u5c06\u5143\u6570\u636e\u6dfb\u52a0\u5230\u4ee3\u7801\u4e2d\u7684\u89c4\u8303\u65b9\u6cd5 \u2014 \u5c5e\u6027 \u2014 \u5982\u679c\u60a8\u613f\u610f\uff0c\u53ef\u4ee5\u5c06\u7aef\u70b9\u6269\u5c55\u65b9\u6cd5\u66ff\u6362\u4e3a\u4e13\u7528\u5c5e\u6027\u3002<\/p>\n<p>Almost all the extension methods that you add to your endpoint have an equivalent attribute you can use instead. These attributes should be applied directly to the handler method (the lambda function, if that\u2019s what you\u2019re using). Listing 11.10 shows the equivalent of listing 11.9, using attributes instead of fluent methods where possible. The WithOpenApi() method is the only call that can\u2019t be replaced; it must be included so that Swashbuckle reads the OpenAPI metadata correctly.<\/p>\n<p>\u60a8\u6dfb\u52a0\u5230\u7ec8\u7aef\u8282\u70b9\u7684\u51e0\u4e4e\u6240\u6709\u6269\u5c55\u65b9\u6cd5\u90fd\u5177\u6709\u60a8\u53ef\u4ee5\u6539\u7528\u7684\u7b49\u6548\u5c5e\u6027\u3002\u8fd9\u4e9b\u5c5e\u6027\u5e94\u76f4\u63a5\u5e94\u7528\u4e8e\u5904\u7406\u7a0b\u5e8f\u65b9\u6cd5\uff08lambda \u51fd\u6570\uff0c\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f lambda \u51fd\u6570\uff09\u3002\u6e05\u5355 11.10 \u663e\u793a\u4e86\u4e0e\u6e05\u5355 11.9 \u7b49\u6548\u7684\u6e05\u5355\uff0c\u5c3d\u53ef\u80fd\u4f7f\u7528\u5c5e\u6027\u800c\u4e0d\u662f\u8fde\u8d2f\u65b9\u6cd5\u3002WithOpenApi\uff08\uff09 \u65b9\u6cd5\u662f\u552f\u4e00\u65e0\u6cd5\u66ff\u6362\u7684\u8c03\u7528;\u5fc5\u987b\u5305\u542b\u5b83\uff0c\u4ee5\u4fbf Swashbuckle \u6b63\u786e\u8bfb\u53d6 OpenAPI \u5143\u6570\u636e\u3002<\/p>\n<p>Listing 11.10 Using attributes to describe your API<br \/>\n\u6e05\u5355 11.10 \u4f7f\u7528\u5c5e\u6027\u6765\u63cf\u8ff0 API<\/p>\n<pre><code>using System.Collections.Concurrent;\n\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nWebApplication app = builder.Build();\n\napp.UseSwagger();\napp.UseSwaggerUI();\n\nvar _fruit = new ConcurrentDictionary&lt;string, Fruit&gt;();\n\napp.MapGet(&quot;\/fruit\/{id}&quot;,\n    [EndpointName(&quot;GetFruit&quot;)]  \u2776\n    [EndpointSummary(&quot;Fetches a fruit&quot;)]  \u2776\n    [EndpointDescription(&quot;Fetches a fruit by id, or returns 404&quot; + \u2776\n        &quot; if no fruit with the ID exists&quot;)]  \u2776\n    [ProducesResponseType(typeof(Fruit), 200)]  \u2776\n    [ProducesResponseType(typeof(HttpValidationProblemDetails), 404, \u2776\n        &quot;application\/problem+json&quot;)]  \u2776\n    [Tags(&quot;fruit&quot;)]  \u2776\n    (string id) =&gt;\n        _fruit.TryGetValue(id, out var fruit)\n            ? TypedResults.Ok(fruit)\n            : Results.Problem(statusCode: 404))\n    .WithOpenApi(o =&gt;\n    {\n        o.Parameters[0].Description = &quot;The id of the fruit to fetch&quot;;\n        return o;\n    });\n\napp.Run();\nrecord Fruit(string Name, int Stock);<\/code><\/pre>\n<p>\u2776 You can use attributes instead of fluent method calls.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528\u5c5e\u6027\u800c\u4e0d\u662f Fluent \u65b9\u6cd5\u8c03\u7528\u3002<\/p>\n<p>Whether you think listing 11.10 is better than listing 11.9 is largely a matter of taste, but the reality is that neither is particularly elegant. In both cases the metadata significantly obscures the intent of the API, so it\u2019s important to consider which metadata is worth adding and which is unnecessary noise. That balance may shift depending on who your audience is (internal or external customers), how mature your API is, and how much you can extract to helper functions.<\/p>\n<p>\u4f60\u662f\u5426\u8ba4\u4e3a\u5217\u51fa 11.10 \u6bd4\u5217\u51fa 11.9 \u66f4\u597d\uff0c\u8fd9\u5728\u5f88\u5927\u7a0b\u5ea6\u4e0a\u662f\u4e00\u4e2a\u54c1\u5473\u95ee\u9898\uff0c\u4f46\u73b0\u5b9e\u662f\u4e24\u8005\u90fd\u4e0d\u662f\u7279\u522b\u4f18\u96c5\u3002\u5728\u8fd9\u4e24\u79cd\u60c5\u51b5\u4e0b\uff0c\u5143\u6570\u636e\u90fd\u4f1a\u4e25\u91cd\u63a9\u76d6 API \u7684\u610f\u56fe\uff0c\u56e0\u6b64\u91cd\u8981\u7684\u662f\u8981\u8003\u8651\u54ea\u4e9b\u5143\u6570\u636e\u503c\u5f97\u6dfb\u52a0\uff0c\u54ea\u4e9b\u662f\u4e0d\u5fc5\u8981\u7684\u5e72\u6270\u3002\u8fd9\u79cd\u5e73\u8861\u53ef\u80fd\u4f1a\u6839\u636e\u60a8\u7684\u53d7\u4f17\u7fa4\u4f53\uff08\u5185\u90e8\u6216\u5916\u90e8\u5ba2\u6237\uff09\u3001API \u7684\u6210\u719f\u5ea6\u4ee5\u53ca\u53ef\u4ee5\u63d0\u53d6\u5230\u5e2e\u52a9\u7a0b\u5e8f\u51fd\u6570\u7684\u6570\u91cf\u3002<\/p>\n<h3>11.5.3 Using XML documentation comments to add metadata<\/h3>\n<h3>11.5.3 \u4f7f\u7528 XML \u6587\u6863\u6ce8\u91ca\u6dfb\u52a0\u5143\u6570\u636e<\/h3>\n<p>One understandable complaint about both the attribute and method approaches for attaching OpenAPI metadata is that the summary and parameter descriptions are divorced from the endpoint handler to which they apply. In this section you\u2019ll see how an alternative approach that uses Extensible Markup Language (XML) documentation comments.<\/p>\n<p>\u5173\u4e8e\u9644\u52a0 OpenAPI \u5143\u6570\u636e\u7684\u5c5e\u6027\u548c\u65b9\u6cd5\u65b9\u6cd5\u7684\u4e00\u4e2a\u53ef\u4ee5\u7406\u89e3\u7684\u62b1\u6028\u662f\uff0c\u6458\u8981\u548c\u53c2\u6570\u63cf\u8ff0\u4e0e\u5b83\u4eec\u6240\u5e94\u7528\u7684\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u5206\u79bb\u3002\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528\u53ef\u6269\u5c55\u6807\u8bb0\u8bed\u8a00 \uff08XML\uff09 \u6587\u6863\u6ce8\u91ca\u7684\u66ff\u4ee3\u65b9\u6cd5\u3002<\/p>\n<p>Every C# developer user will be used to the handy descriptions about methods and parameters you get in your IDE from IntelliSense. You can add these descriptions to your own methods by using XML documentation comments, for example:<\/p>\n<p>\u6bcf\u4e2a C# \u5f00\u53d1\u4eba\u5458\u7528\u6237\u90fd\u5c06\u4e60\u60ef\u4e8e\u4ece IntelliSense \u83b7\u5f97\u7684\u6709\u5173 IDE \u4e2d\u7684\u65b9\u6cd5\u548c\u53c2\u6570\u7684\u4fbf\u6377\u8bf4\u660e\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 XML \u6587\u6863\u6ce8\u91ca\u5c06\u8fd9\u4e9b\u63cf\u8ff0\u6dfb\u52a0\u5230\u60a8\u81ea\u5df1\u7684\u65b9\u6cd5\u4e2d\uff0c\u4f8b\u5982\uff1a<\/p>\n<pre><code>\/\/\/ &lt;summary&gt;\n\/\/\/ Adds one to the provided value and returns it\n\/\/\/ &lt;\/summary&gt;\n\/\/\/ &lt;param name=&quot;value&quot;&gt;The value to increment&lt;\/param&gt;\npublic int Increment(int value) =&gt; value + 1;<\/code><\/pre>\n<p>In your IDE\u2014whether that\u2019s Visual Studio, JetBrains Rider, or Visual Studio Code\u2014this description appears when you try to invoke the method. Wouldn\u2019t it be nice to use the same syntax to define the summary and parameter descriptions for our OpenAPI endpoints? Well, the good news is that we can!<\/p>\n<p>\u5728\u60a8\u7684 IDE \u4e2d\uff08\u65e0\u8bba\u662f Visual Studio\u3001JetBrains Rider \u8fd8\u662f Visual Studio Code\uff09\uff0c\u5f53\u60a8\u5c1d\u8bd5\u8c03\u7528\u8be5\u65b9\u6cd5\u65f6\uff0c\u4f1a\u663e\u793a\u6b64\u63cf\u8ff0\u3002\u4f7f\u7528\u76f8\u540c\u7684\u8bed\u6cd5\u6765\u5b9a\u4e49 OpenAPI \u7aef\u70b9\u7684\u6458\u8981\u548c\u53c2\u6570\u63cf\u8ff0\u4e0d\u662f\u5f88\u597d\u5417\uff1f\u597d\u5427\uff0c\u597d\u6d88\u606f\u662f\u6211\u4eec\u53ef\u4ee5\uff01<\/p>\n<p><strong>Warning<\/strong> The use of XML documentation comments is only partially supported in .NET 7. These comments work only when you have static or instance method endpoint handlers, not lambda methods or local functions. You can find the issue tracking full support for XML comments at <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/39927\">https:\/\/github.com\/dotnet\/aspnetcore\/issues\/39927<\/a>.<br \/>\n<strong>\u8b66\u544a<\/strong> .NET 7 \u4ec5\u90e8\u5206\u652f\u6301\u4f7f\u7528 XML \u6587\u6863\u6ce8\u91ca\u3002\u8fd9\u4e9b\u6ce8\u91ca\u4ec5\u5728\u60a8\u5177\u6709\u9759\u6001\u6216\u5b9e\u4f8b\u65b9\u6cd5\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\uff0c\u800c\u4e0d\u662f lambda \u65b9\u6cd5\u6216\u672c\u5730\u51fd\u6570\u65f6\u6709\u6548\u3002\u60a8\u53ef\u4ee5\u627e\u5230\u95ee\u9898\u5728 <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/39927\">https:\/\/github.com\/dotnet\/aspnetcore\/issues\/39927<\/a> \u8ddf\u8e2a\u5bf9 XML \u6ce8\u91ca\u7684\u5b8c\u5168\u652f\u6301\u3002<\/p>\n<p>Swashbuckle can use the XML comments you add to your endpoint handlers as the descriptions for your OpenAPI description. When enabled, the .NET software development kit (SDK) generates an XML file containing all your documentation comments. Swashbuckle can read this file on startup and use it to generate the OpenAPI descriptions, as shown in figure 11.11.<\/p>\n<p>Swashbuckle \u53ef\u4ee5\u4f7f\u7528\u60a8\u6dfb\u52a0\u5230\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u7684 XML \u6ce8\u91ca\u4f5c\u4e3a OpenAPI \u63cf\u8ff0\u7684\u63cf\u8ff0\u3002\u542f\u7528\u540e\uff0c.NET SDK \u4f1a\u751f\u6210\u4e00\u4e2a\u5305\u542b\u6240\u6709\u6587\u6863\u6ce8\u91ca\u7684 XML \u6587\u4ef6\u3002Swashbuckle \u53ef\u4ee5\u5728\u542f\u52a8\u65f6\u8bfb\u53d6\u6b64\u6587\u4ef6\u5e76\u4f7f\u7528\u5b83\u6765\u751f\u6210 OpenAPI \u63cf\u8ff0\uff0c\u5982\u56fe 11.11 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1111.png\" alt=\"alt text \" \/><\/p>\n<p>Figure 11.11 You can configure a .NET application to export documentation comments to a dedicated XML file when it builds. Swashbuckle reads this documentation file at runtime, combining it with the attribute and fluent method metadata for an endpoint to generate the final OpenAPI description.<br \/>\n\u56fe 11.11 \u60a8\u53ef\u4ee5\u914d\u7f6e .NET \u5e94\u7528\u7a0b\u5e8f\u4ee5\u5c06\u6587\u6863\u6ce8\u91ca\u5bfc\u51fa\u5230\u4e13\u7528 XML \u6587\u4ef6\u5f53\u5b83\u6784\u5efa\u65f6\u3002Swashbuckle \u5728\u8fd0\u884c\u65f6\u8bfb\u53d6\u6b64\u6587\u6863\u6587\u4ef6\uff0c\u5c06\u5176\u4e0e\u7ec8\u7aef\u8282\u70b9\u7684\u5c5e\u6027\u548c Fluent \u65b9\u6cd5\u5143\u6570\u636e\u76f8\u7ed3\u5408\uff0c\u4ee5\u751f\u6210\u6700\u7ec8\u7684 OpenAPI \u63cf\u8ff0\u3002<\/p>\n<p>To enable XML documentation comment extraction for your OpenAPI description document you must do three things:<br \/>\n\u8981\u4e3a\u60a8\u7684 OpenAPI \u63cf\u8ff0\u6587\u6863\u542f\u7528 XML \u6587\u6863\u6ce8\u91ca\u63d0\u53d6\uff0c\u60a8\u5fc5\u987b\u6267\u884c\u4ee5\u4e0b\u4e09\u9879\u4f5c\uff1a<\/p>\n<ol>\n<li>Enable documentation generation for your project. Add the <code>&lt;GenerateDocumentationFile&gt;<\/code> inside a <code>&lt;PropertyGroup&gt;<\/code> in your csproj file, and set it to true:<br \/>\n\u4e3a\u60a8\u7684\u9879\u76ee\u542f\u7528\u6587\u6863\u751f\u6210\u3002\u5728 <code>&lt;GenerateDocumentationFile&gt;<\/code> \u4e2d\u6dfb\u52a0<code>&lt;PropertyGroup&gt;<\/code>\uff0c\u5e76\u5c06\u5176\u8bbe\u7f6e\u4e3a<\/li>\n<\/ol>\n<p>true\uff1a<\/p>\n<pre><code>&lt;PropertyGroup&gt;\n  &lt;GenerateDocumentationFile&gt;true&lt;\/GenerateDocumentationFile&gt;\n&lt;\/PropertyGroup&gt;<\/code><\/pre>\n<ol start=\"2\">\n<li>Configure Swashbuckle to read the generated XML document in SwaggerGen():<br \/>\n\u914d\u7f6e Swashbuckle \u4ee5\u8bfb\u53d6 SwaggerGen\uff08\uff09 \u4e2d\u751f\u6210\u7684 XML \u6587\u6863\uff1a<\/li>\n<\/ol>\n<pre><code>builder.Services.AddSwaggerGen(opts =&gt;\n{\n    var file = $&quot;{Assembly.GetExecutingAssembly().GetName().Name}.xml&quot;;\n    opts.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, file));\n});<\/code><\/pre>\n<p>3.Use a static or instance method handler and add the XML comments, as shown in the following listing.<br \/>\n\u4f7f\u7528\u9759\u6001\u6216\u5b9e\u4f8b\u65b9\u6cd5\u5904\u7406\u7a0b\u5e8f\u5e76\u6dfb\u52a0 XML \u6ce8\u91ca\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 11.11 Adding documentation comments to an endpoint handler<br \/>\nListing 11.11 \u5411\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u6dfb\u52a0\u6587\u6863\u6ce8\u91ca<\/p>\n<pre><code>using Microsoft.AspNetCore.Mvc;\nusing System.Collections.Concurrent;\nusing System.Reflection;\n\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen(opts =&gt;  \u2776\n{\n    var file = $&quot;{Assembly.GetExecutingAssembly().GetName().Name}.xml&quot;;\n    opts.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, file));\n});\n\nWebApplication app = builder.Build();\n\napp.UseSwagger();\napp.UseSwaggerUI();\n\nvar _fruit = new ConcurrentDictionary&lt;string, Fruit&gt;();\n\nvar handler = new FruitHandler(fruit);  \u2777\napp.MapGet(&quot;\/fruit\/{id}&quot;, handler.GetFruit)  \u2777\n    .WithName(&quot;GetFruit&quot;);  \u2778\n\napp.Run();\nrecord Fruit(string Name, int Stock);\n\ninternal class FruitHandler\n{\n    private readonly ConcurrentDictionary&lt;string, Fruit&gt; _fruit;\n    public FruitHandler(ConcurrentDictionary&lt;string, Fruit&gt; fruit)\n    {\n        _fruit = fruit;\n    }\n\n    \/\/\/ &lt;summary&gt;    \u2779\n    \/\/\/ Fetches a fruit by id, or returns 404 if it does not exist   \u2779\n    \/\/\/ &lt;\/summary&gt;    \u2779\n    \/\/\/ &lt;param name=&quot;id&quot; &gt;The ID of the fruit to fetch&lt;\/param&gt;    \u2779\n    \/\/\/ &lt;response code=&quot;200&quot;&gt;Returns the fruit if it exists&lt;\/response&gt;  \u2779\n    \/\/\/ &lt;response code=&quot;404&quot;&gt;If the fruit doesn&#039;t exist&lt;\/response&gt;   \u2779\n    [ProducesResponseType(typeof(Fruit), 200)]    \u277a\n    [ProducesResponseType(typeof(HttpValidationProblemDetails),   \u277a\n        404, &quot;application\/problem+json&quot;)]    \u277a\n    [Tags(&quot;fruit&quot;)]    \u277a\n    public IResult GetFruit(string id)\n        =&gt; _fruit.TryGetValue(id, out var fruit)\n            ? TypedResults.Ok(fruit)\n            : Results.Problem(statusCode: 404);\n}<\/code><\/pre>\n<p>\u2776 Enables XML comments for your OpenAPI descriptions<br \/>\n\u4e3a\u60a8\u7684 OpenAPI \u63cf\u8ff0\u542f\u7528 XML \u6ce8\u91ca<br \/>\n\u2777 You must use static or instance handlers, not lambda methods.<br \/>\n\u60a8\u5fc5\u987b\u4f7f\u7528\u9759\u6001\u6216\u5b9e\u4f8b\u5904\u7406\u7a0b\u5e8f\uff0c\u800c\u4e0d\u662f lambda \u65b9\u6cd5\u3002<br \/>\n\u2778 You can add extra metadata by using methods.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 methods \u6dfb\u52a0\u989d\u5916\u7684\u5143\u6570\u636e\u3002<br \/>\n\u2779 The XML comments are used in the OpenAPI description.<br \/>\nXML \u6ce8\u91ca\u7528\u4e8e OpenAPI \u63cf\u8ff0\u3002<br \/>\n\u277a You can also add extra metadata by using attributes on the handler method.<br \/>\n\u60a8\u8fd8\u53ef\u4ee5\u901a\u8fc7\u5728 handler \u65b9\u6cd5\u4e0a\u4f7f\u7528 attributes \u6765\u6dfb\u52a0\u989d\u5916\u7684\u5143\u6570\u636e\u3002<\/p>\n<p>I like the XML comment approach, as it feels much more natural for C# and the comments are often deemphasized in IDEs, reducing visual clutter. You\u2019ll still need to use attributes and\/or fluent methods to fully describe your endpoints for OpenAPI, but every little bit helps!<\/p>\n<p>\u6211\u559c\u6b22 XML \u6ce8\u91ca\u65b9\u6cd5\uff0c\u56e0\u4e3a\u5b83\u5bf9 C# \u6765\u8bf4\u611f\u89c9\u66f4\u81ea\u7136\uff0c\u800c\u4e14\u6ce8\u91ca\u5728 IDE \u4e2d\u7ecf\u5e38\u88ab\u6de1\u5316\uff0c\u4ece\u800c\u51cf\u5c11\u4e86\u89c6\u89c9\u6df7\u4e71\u3002\u60a8\u4ecd\u7136\u9700\u8981\u4f7f\u7528\u5c5e\u6027\u548c\/\u6216 Fluent \u65b9\u6cd5\u6765\u5b8c\u6574\u63cf\u8ff0 OpenAPI \u7684\u7aef\u70b9\uff0c\u4f46\u6bcf\u4e00\u70b9\u90fd\u6709\u5e2e\u52a9\uff01<\/p>\n<p>As I\u2019ve mentioned several times, how far you go with your OpenAPI description is up to you and how much value you get from it. If you want to use OpenAPI only for local testing with Swagger UI, it doesn\u2019t make sense to clutter your code with lots of extra metadata. In fact, in those cases it would be best to add the swagger services and middleware conditionally only when you\u2019re in development, as in this example:<\/p>\n<p>\u6b63\u5982\u6211\u591a\u6b21\u63d0\u5230\u7684\uff0c\u60a8\u5bf9 OpenAPI \u63cf\u8ff0\u7684\u4e86\u89e3\u7a0b\u5ea6\u53d6\u51b3\u4e8e\u60a8\uff0c\u4ee5\u53ca\u60a8\u4ece\u4e2d\u83b7\u5f97\u591a\u5c11\u4ef7\u503c\u3002\u5982\u679c\u60a8\u53ea\u60f3\u5c06 OpenAPI \u7528\u4e8e Swagger UI \u7684\u672c\u5730\u6d4b\u8bd5\uff0c\u90a3\u4e48\u7528\u5927\u91cf\u989d\u5916\u7684\u5143\u6570\u636e\u6765\u6742\u4e71\u65e0\u7ae0\u7684\u4ee3\u7801\u662f\u6ca1\u6709\u610f\u4e49\u7684\u3002\u4e8b\u5b9e\u4e0a\uff0c\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u6700\u597d\u4ec5\u5728\u5f00\u53d1\u8fc7\u7a0b\u4e2d\u6709\u6761\u4ef6\u5730\u6dfb\u52a0 swagger \u670d\u52a1\u548c\u4e2d\u95f4\u4ef6\uff0c\u5982\u4e0b\u4f8b\u6240\u793a\uff1a<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nif(builder.Environment.IsDevelopment())\n{\n    builder.Services.AddEndpointsApiExplorer();\n    builder.Services.AddSwaggerGen();\n}\n\nWebApplication app = builder.Build();\nif(app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.Run();<\/code><\/pre>\n<p>On the other hand, if you\u2019re generating C# clients for calling your API or exposing your API for public consumption, the more metadata you add, the better! It\u2019s also worth noting that you can add OpenAPI descriptions for all the endpoints in your application, not only your minimal API endpoints. When you create web API controllers in chapter 20, you can include them = too.<\/p>\n<p>\u53e6\u4e00\u65b9\u9762\uff0c\u5982\u679c\u8981\u751f\u6210 C# \u5ba2\u6237\u7aef\u6765\u8c03\u7528 API \u6216\u516c\u5f00 API \u4ee5\u4f9b\u516c\u4f17\u4f7f\u7528\uff0c\u5219\u6dfb\u52a0\u7684\u5143\u6570\u636e\u8d8a\u591a\u8d8a\u597d\uff01\u8fd8\u503c\u5f97\u6ce8\u610f\u7684\u662f\uff0c\u60a8\u53ef\u4ee5\u4e3a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0 OpenAPI \u63cf\u8ff0\uff0c\u800c\u4e0d\u4ec5\u4ec5\u662f\u60a8\u7684\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\u3002\u5728\u7b2c 20 \u7ae0\u4e2d\u521b\u5efa Web API \u63a7\u5236\u5668\u65f6\uff0c\u4e5f\u53ef\u4ee5\u5305\u542b\u5b83\u4eec\u3002<\/p>\n<h3>11.6 Knowing the limitations of OpenAPI<\/h3>\n<h3>11.6 \u4e86\u89e3 OpenAPI \u7684\u5c40\u9650\u6027<\/h3>\n<p>In this chapter I\u2019ve described the benefits of OpenAPI, both for simple testing with Swagger UI and for code generation. But like most things in software, it\u2019s not all sweetness and light. OpenAPI and Swagger have limitations that you may run into, particularly as your APIs increase in complexity. In this section I describe some of the challenges to watch out for.<\/p>\n<p>\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4ecb\u7ecd\u4e86 OpenAPI \u7684\u597d\u5904\uff0c\u65e0\u8bba\u662f\u4f7f\u7528 Swagger UI \u8fdb\u884c\u7b80\u5355\u6d4b\u8bd5\u8fd8\u662f\u4ee3\u7801\u751f\u6210\u3002\u4f46\u5c31\u50cf\u8f6f\u4ef6\u4e2d\u7684\u5927\u591a\u6570\u4e1c\u897f\u4e00\u6837\uff0c\u5b83\u5e76\u4e0d\u5168\u662f\u751c\u871c\u548c\u8f7b\u677e\u3002OpenAPI \u548c Swagger \u5b58\u5728\u60a8\u53ef\u80fd\u4f1a\u9047\u5230\u7684\u9650\u5236\uff0c\u5c24\u5176\u662f\u5728 API \u590d\u6742\u6027\u589e\u52a0\u65f6\u3002\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u4e00\u4e9b\u9700\u8981\u6ce8\u610f\u7684\u6311\u6218\u3002<\/p>\n<h3>11.6.1 Not all APIs can be described by OpenAPI<\/h3>\n<h3>11.6.1 \u5e76\u975e\u6240\u6709 API \u90fd\u53ef\u4ee5\u7531 OpenAPI \u63cf\u8ff0<\/h3>\n<p>The OpenAPI specification is meant to describe your API so that any client knows how to call it. Unfortunately, OpenAPI can\u2019t describe all APIs, which isn\u2019t an accident. The OpenAPI specification says \u201cNot all services can be described by OpenAPI\u2014this specification is not intended to cover every possible style of REST APIs.\u201d So,= the important question is which APIs can\u2019t it describe?<\/p>\n<p>OpenAPI \u89c4\u8303\u65e8\u5728\u63cf\u8ff0\u60a8\u7684 API\uff0c\u4ee5\u4fbf\u4efb\u4f55\u5ba2\u6237\u7aef\u90fd\u77e5\u9053\u5982\u4f55\u8c03\u7528\u5b83\u3002\u4e0d\u5e78\u7684\u662f\uff0cOpenAPI \u65e0\u6cd5\u63cf\u8ff0\u6240\u6709 API\uff0c\u8fd9\u5e76\u975e\u5076\u7136\u3002OpenAPI \u89c4\u8303\u6307\u51fa\u201c\u5e76\u975e\u6240\u6709\u670d\u52a1\u90fd\u53ef\u4ee5\u7531 OpenAPI \u63cf\u8ff0\u2014 \u6b64\u89c4\u8303\u5e76\u4e0d\u6253\u7b97\u6db5\u76d6\u6240\u6709\u53ef\u80fd\u7684 REST API \u6837\u5f0f\u3002\u90a3\u4e48\uff0c\u91cd\u8981\u7684\u95ee\u9898\u662f\u54ea\u4e9b API \u4e0d\u80fd\u63cf\u8ff0\uff1f<\/p>\n<p>One classic example is an API that follows the REST design known as Hypertext As the Engine of Application State (HATEOAS). In this design, each request to an API endpoint includes a list of links describing the actions you can take and the paths to use for each action, enabling clients to discover which actions are available for a given resource. The server can add or remove links dynamically, depending on the state of the resource and which user is making the request.<\/p>\n<p>\u4e00\u4e2a\u5178\u578b\u7684\u793a\u4f8b\u662f\u9075\u5faa REST \u8bbe\u8ba1\u7684 API\uff0c\u79f0\u4e3a\u8d85\u6587\u672c\u4f5c\u4e3a\u5e94\u7528\u7a0b\u5e8f\u72b6\u6001\u5f15\u64ce \uff08HATEOAS\uff09\u3002\u5728\u6b64\u8bbe\u8ba1\u4e2d\uff0c\u5bf9 API \u7ec8\u7aef\u8282\u70b9\u7684\u6bcf\u4e2a\u8bf7\u6c42\u90fd\u5305\u542b\u4e00\u4e2a\u94fe\u63a5\u5217\u8868\uff0c\u8fd9\u4e9b\u94fe\u63a5\u63cf\u8ff0\u4e86\u60a8\u53ef\u4ee5\u6267\u884c\u7684\u4f5c\u4ee5\u53ca\u7528\u4e8e\u6bcf\u4e2a\u4f5c\u7684\u8def\u5f84\uff0c\u4f7f\u5ba2\u6237\u7aef\u80fd\u591f\u53d1\u73b0\u54ea\u4e9b\u4f5c\u53ef\u7528\u4e8e\u7ed9\u5b9a\u8d44\u6e90\u3002\u670d\u52a1\u5668\u53ef\u4ee5\u52a8\u6001\u6dfb\u52a0\u6216\u5220\u9664\u94fe\u63a5\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u8d44\u6e90\u7684\u72b6\u6001\u548c\u53d1\u51fa\u8bf7\u6c42\u7684\u7528\u6237\u3002<\/p>\n<p><strong>Tip<\/strong> Martin Fowler has a great description of the REST maturity models, in which HATEOAS is the highest level of maturity, at <a href=\"http:\/\/mng.bz\/0K1N\">http:\/\/mng.bz\/0K1N<\/a>.<br \/>\n<strong>\u63d0\u793a<\/strong> Martin Fowler \u5bf9 REST \u6210\u719f\u5ea6\u6a21\u578b\u8fdb\u884c\u4e86\u5f88\u597d\u7684\u63cf\u8ff0\uff0c\u5176\u4e2d HATEOAS \u662f\u6700\u9ad8\u7ea7\u522b\u7684\u6210\u719f\u5ea6\uff0c\u8fbe\u5230 <a href=\"http:\/\/mng.bz\/0K1N\">http:\/\/mng.bz\/0K1N<\/a>\u3002<\/p>\n<p>HATEOAS generally introduces more complexity than is worthwhile for small projects, but it\u2019s a great way to decouple your client-side applications from your server APIs so that they can evolve separately. This approach can be invaluable when you have large or independent teams. The problem for OpenAPI is that it wasn\u2019t designed for these kinds of dynamic APIs. OpenAPI wants to know up front what the responses are for each of your endpoints, which isn\u2019t information that you can give it if you\u2019re following HATEOAS.<\/p>\n<p>HATEOAS \u901a\u5e38\u4f1a\u5e26\u6765\u6bd4\u5c0f\u578b\u9879\u76ee\u6240\u503c\u5f97\u7684\u66f4\u591a\u7684\u590d\u6742\u6027\uff0c\u4f46\u8fd9\u662f\u5c06\u5ba2\u6237\u7aef\u5e94\u7528\u7a0b\u5e8f\u4e0e\u670d\u52a1\u5668 API \u5206\u79bb\u7684\u597d\u65b9\u6cd5\uff0c\u4ee5\u4fbf\u5b83\u4eec\u53ef\u4ee5\u5355\u72ec\u53d1\u5c55\u3002\u5f53\u60a8\u62e5\u6709\u5927\u578b\u6216\u72ec\u7acb\u56e2\u961f\u65f6\uff0c\u8fd9\u79cd\u65b9\u6cd5\u53ef\u80fd\u975e\u5e38\u5b9d\u8d35\u3002OpenAPI \u7684\u95ee\u9898\u5728\u4e8e\u5b83\u4e0d\u662f\u4e3a\u8fd9\u4e9b\u7c7b\u578b\u7684\u52a8\u6001 API \u8bbe\u8ba1\u7684\u3002OpenAPI \u5e0c\u671b\u63d0\u524d\u77e5\u9053\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u7684\u54cd\u5e94\u662f\u4ec0\u4e48\uff0c\u5982\u679c\u60a8\u9075\u5faa HATEOAS\uff0c\u5219\u65e0\u6cd5\u63d0\u4f9b\u8fd9\u4e9b\u4fe1\u606f\u3002<\/p>\n<p>In a different scenario, you may have multiple backend APIs, each with its own OpenAPI specification. You expose a single, unified API gateway app, with which all your clients interact. Unfortunately, even though each backend API has an OpenAPI specification, there\u2019s no easy way to combine the APIs into a single unified document that you can expose in your API gateway and which clients can use for testing and code generation.<\/p>\n<p>\u5728\u4e0d\u540c\u7684\u60c5\u51b5\u4e0b\uff0c\u60a8\u53ef\u80fd\u6709\u591a\u4e2a\u540e\u7aef API\uff0c\u6bcf\u4e2a API \u90fd\u6709\u81ea\u5df1\u7684 OpenAPI \u89c4\u8303\u3002\u60a8\u516c\u5f00\u4e00\u4e2a\u7edf\u4e00\u7684 API Gateway \u5e94\u7528\u7a0b\u5e8f\uff0c\u6240\u6709\u5ba2\u6237\u7aef\u90fd\u4e0e\u8be5\u5e94\u7528\u7a0b\u5e8f\u8fdb\u884c\u4ea4\u4e92\u3002\u9057\u61be\u7684\u662f\uff0c\u5373\u4f7f\u6bcf\u4e2a\u540e\u7aef API \u90fd\u6709\u4e00\u4e2a OpenAPI \u89c4\u8303\uff0c\u4e5f\u6ca1\u6709\u7b80\u5355\u7684\u65b9\u6cd5\u53ef\u4ee5\u5c06\u8fd9\u4e9b API \u7ec4\u5408\u6210\u4e00\u4e2a\u7edf\u4e00\u7684\u6587\u6863\uff0c\u60a8\u53ef\u4ee5\u5728 API \u7f51\u5173\u4e2d\u516c\u5f00\u8be5\u6587\u6863\uff0c\u5ba2\u6237\u7aef\u53ef\u4ee5\u4f7f\u7528\u8be5\u6587\u6863\u8fdb\u884c\u6d4b\u8bd5\u548c\u4ee3\u7801\u751f\u6210\u3002<\/p>\n<p>Another common problem centers on securing your APIs with authentication and authorization. The OpenAPI specification contains a section about describing your authentication requirements, and Swagger UI supports them. Where things fall down is if you\u2019re using any extensions to the common authentication protocols or advanced features. Although some of these workflows are possible, in some cases Swagger UI simply may not support your workflow, rendering Swagger UI unusable.<\/p>\n<p>\u53e6\u4e00\u4e2a\u5e38\u89c1\u95ee\u9898\u96c6\u4e2d\u5728\u4f7f\u7528\u8eab\u4efd\u9a8c\u8bc1\u548c\u6388\u6743\u4fdd\u62a4\u60a8\u7684 API \u4e0a\u3002OpenAPI \u89c4\u8303\u5305\u542b\u6709\u5173\u63cf\u8ff0\u8eab\u4efd\u9a8c\u8bc1\u7684\u90e8\u5206\u8981\u6c42\uff0c\u5e76\u4e14 Swagger UI \u652f\u6301\u5b83\u4eec\u3002\u51fa\u73b0\u95ee\u9898\u7684\u5730\u65b9\u662f\u5982\u679c\u60a8\u4f7f\u7528\u4e86\u5e38\u89c1\u8eab\u4efd\u9a8c\u8bc1\u534f\u8bae\u6216\u9ad8\u7ea7\u529f\u80fd\u7684\u4efb\u4f55\u6269\u5c55\u3002\u5c3d\u7ba1\u5176\u4e2d\u4e00\u4e9b\u5de5\u4f5c\u6d41\u7a0b\u662f\u53ef\u80fd\u7684\uff0c\u4f46\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0cSwagger UI \u53ef\u80fd\u6839\u672c\u4e0d\u652f\u6301\u60a8\u7684\u5de5\u4f5c\u6d41\u7a0b\uff0c\u4ece\u800c\u5bfc\u81f4 Swagger UI \u4e0d\u53ef\u7528\u3002<\/p>\n<h3>11.6.2 Generated code is opinionated<\/h3>\n<h3>11.6.2 \u751f\u6210\u7684\u4ee3\u7801\u662f\u56fa\u6267\u5df1\u89c1\u7684<\/h3>\n<p>At the end of section 11.4 I said that code generation is the killer feature for Open API documents, and in many cases it is. That statement, however, assumes that you like the generated code. If the tooling you use\u2014whether that\u2019s NSwag or some other code generator\u2014doesn\u2019t generate the code you want, you may find yourself spending a lot of effort customizing and tweaking the output. At some point and for some APIs, it may be simpler and easier to write your own client!<\/p>\n<p>\u5728 11.4 \u8282\u7684\u7ed3\u5c3e\uff0c\u6211\u8bf4\u8fc7\u4ee3\u7801\u751f\u6210\u662f Open API \u6587\u6863\u7684\u6740\u624b\u7ea7\u529f\u80fd\uff0c\u5728\u8bb8\u591a\u60c5\u51b5\u4e0b\u786e\u5b9e\u5982\u6b64\u3002\u4f46\u662f\uff0c\u8be5\u8bed\u53e5\u5047\u5b9a\u60a8\u559c\u6b22\u751f\u6210\u7684\u4ee3\u7801\u3002\u5982\u679c\u60a8\u4f7f\u7528\u7684\u5de5\u5177\uff08\u65e0\u8bba\u662f NSwag \u8fd8\u662f\u5176\u4ed6\u4ee3\u7801\u751f\u6210\u5668\uff09\u6ca1\u6709\u751f\u6210\u60a8\u60f3\u8981\u7684\u4ee3\u7801\uff0c\u60a8\u53ef\u80fd\u4f1a\u53d1\u73b0\u81ea\u5df1\u82b1\u8d39\u4e86\u5927\u91cf\u7cbe\u529b\u6765\u81ea\u5b9a\u4e49\u548c\u8c03\u6574\u8f93\u51fa\u3002\u5728\u67d0\u4e9b\u65f6\u5019\uff0c\u5bf9\u4e8e\u67d0\u4e9b API\uff0c\u7f16\u5199\u81ea\u5df1\u7684\u5ba2\u6237\u7aef\u53ef\u80fd\u4f1a\u66f4\u7b80\u5355\u3001\u66f4\u5bb9\u6613\uff01<\/p>\n<p><strong>Note<\/strong> A classic complaint (with which I sympathize) is the use of exceptions for process flow whenever an error or unexpected status code is returned. Not all errors are exceptional, throwing exceptions is relatively expensive computationally, and it often means that every call made with a client needs custom exception handling. This design sometimes makes code generation seem more like a burden than a benefit.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u4e00\u4e2a\u5178\u578b\u7684\u62b1\u6028\uff08\u6211\u5bf9\u6b64\u8868\u793a\u540c\u60c5\uff09\u662f\u6bcf\u5f53\u8fd4\u56de\u9519\u8bef\u6216\u610f\u5916\u72b6\u6001\u4ee3\u7801\u65f6\uff0c\u90fd\u4f1a\u5bf9\u6d41\u7a0b\u4f7f\u7528\u5f02\u5e38\u3002\u5e76\u975e\u6240\u6709\u9519\u8bef\u90fd\u662f\u5f02\u5e38\u7684\uff0c\u5f15\u53d1\u5f02\u5e38\u7684\u8ba1\u7b97\u6210\u672c\u76f8\u5bf9\u8f83\u9ad8\uff0c\u8fd9\u901a\u5e38\u610f\u5473\u7740\u4f7f\u7528\u5ba2\u6237\u7aef\u8fdb\u884c\u7684\u6bcf\u4e2a\u8c03\u7528\u90fd\u9700\u8981\u81ea\u5b9a\u4e49\u5f02\u5e38\u5904\u7406\u3002\u8fd9\u79cd\u8bbe\u8ba1\u6709\u65f6\u4f7f\u4ee3\u7801\u751f\u6210\u770b\u8d77\u6765\u66f4\u50cf\u662f\u4e00\u79cd\u8d1f\u62c5\uff0c\u800c\u4e0d\u662f\u4e00\u79cd\u597d\u5904\u3002<\/p>\n<p>Another, subtler issue arises when you use code generation with two separate but related OpenAPI documents, such as a products API and a cart API. If you use the techniques in this chapter to generate the clients and then try to follow this simple sequence, you\u2019ll run into trouble:<\/p>\n<p>\u5f53\u60a8\u5c06\u4ee3\u7801\u751f\u6210\u4e0e\u4e24\u4e2a\u72ec\u7acb\u4f46\u76f8\u5173\u7684 OpenAPI \u6587\u6863\uff08\u4f8b\u5982\u4ea7\u54c1 API \u548c\u8d2d\u7269\u8f66 API\uff09\u4e00\u8d77\u4f7f\u7528\u65f6\uff0c\u4f1a\u51fa\u73b0\u53e6\u4e00\u4e2a\u66f4\u5fae\u5999\u7684\u95ee\u9898\u3002\u5982\u679c\u4f7f\u7528\u672c\u7ae0\u4e2d\u7684\u6280\u672f\u751f\u6210\u5ba2\u6237\u7aef\uff0c\u7136\u540e\u5c1d\u8bd5\u9075\u5faa\u4ee5\u4e0b\u7b80\u5355\u987a\u5e8f\uff0c\u5219\u4f1a\u9047\u5230\u9ebb\u70e6\uff1a<\/p>\n<ol>\n<li>\n<p>Retrieve a Product instance from the products API by using ProductsClient.Get()<br \/>\n\u4f7f\u7528 ProductsClient.Get\uff08\uff09 \u4ece products API \u68c0\u7d22 Product \u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<li>\n<p>Send the retrieved Product to the cart API by using CartClient.Add(Product)<br \/>\n\u4f7f\u7528 \u5c06\u68c0\u7d22\u5230\u7684 Product \u53d1\u9001\u5230\u8d2d\u7269\u8f66 API CartClient.Add\uff08Product\uff09 \u7684<\/p>\n<\/li>\n<\/ol>\n<p>Unfortunately, the generated Product type retrieved from the products API is a different type from the generated Product type that the CartClient requires, so this code won\u2019t compile. Even if the type has the same properties and is serialized to the same JSON when it\u2019s sent to the client, C# considers the objects to be different types and won\u2019t let them swap places. You must copy the values manually from the first Product instance to a new instance. These complaints are mostly small niggles and paper cuts, but they can add up when you run into them often.<\/p>\n<p>\u9057\u61be\u7684\u662f\uff0c\u4ece products API \u68c0\u7d22\u5230\u7684\u751f\u6210\u7684 Product \u7c7b\u578b\u4e0e CartClient \u9700\u8981\u7684\u751f\u6210\u7684 Product \u7c7b\u578b\u4e0d\u540c\uff0c\u56e0\u6b64\u6b64\u4ee3\u7801\u65e0\u6cd5\u7f16\u8bd1\u3002\u5373\u4f7f\u7c7b\u578b\u5177\u6709\u76f8\u540c\u7684\u5c5e\u6027\uff0c\u5e76\u4e14\u5728\u53d1\u9001\u5230\u5ba2\u6237\u7aef\u65f6\u5e8f\u5217\u5316\u4e3a\u76f8\u540c\u7684 JSON\uff0cC# \u4e5f\u4f1a\u5c06\u5bf9\u8c61\u89c6\u4e3a\u4e0d\u540c\u7684\u7c7b\u578b\uff0c\u5e76\u4e14\u4e0d\u5141\u8bb8\u5b83\u4eec\u4ea4\u6362\u4f4d\u7f6e\u3002\u60a8\u5fc5\u987b\u624b\u52a8\u5c06\u503c\u4ece\u7b2c\u4e00\u4e2a Product \u5b9e\u4f8b\u590d\u5236\u5230\u65b0\u5b9e\u4f8b\u3002\u8fd9\u4e9b\u62b1\u6028\u5927\u591a\u662f\u5c0f\u95ee\u9898\u548c\u526a\u7eb8\uff0c\u4f46\u5f53\u60a8\u7ecf\u5e38\u9047\u5230\u5b83\u4eec\u65f6\uff0c\u5b83\u4eec\u4f1a\u7d2f\u79ef\u8d77\u6765\u3002<\/p>\n<h3>11.6.3 Tooling often lags the specification<\/h3>\n<h3>11.6.3 \u5de5\u5177\u7ecf\u5e38\u6ede\u540e\u4e8e\u89c4\u683c<\/h3>\n<p>Another factor to consider is the many groups that are involved in generating an OpenAPI document and generating a client:<\/p>\n<p>\u53e6\u4e00\u4e2a\u9700\u8981\u8003\u8651\u7684\u56e0\u7d20\u662f\u751f\u6210 OpenAPI \u6587\u6863\u548c\u751f\u6210\u5ba2\u6237\u7aef\u6240\u6d89\u53ca\u7684\u8bb8\u591a\u7ec4\uff1a<\/p>\n<ul>\n<li>\n<p>The Open API specification is a community-driven project written by the OpenAPI Initiative group.<br \/>\nOpen API \u89c4\u8303\u662f\u7531 OpenAPI Initiative \u5c0f\u7ec4\u7f16\u5199\u7684\u793e\u533a\u9a71\u52a8\u578b\u9879\u76ee\u3002<\/p>\n<\/li>\n<li>\n<p>Microsoft provides the tooling built into ASP.NET Core for supplying the metadata about your API endpoints.<br \/>\nMicrosoft \u63d0\u4f9b\u4e86 ASP.NET Core \u4e2d\u5185\u7f6e\u7684\u5de5\u5177\uff0c\u7528\u4e8e\u63d0\u4f9b\u6709\u5173 API \u7ec8\u7aef\u8282\u70b9\u7684\u5143\u6570\u636e\u3002<\/p>\n<\/li>\n<li>\n<p>Swashbuckle is an open-source project that uses the ASP.NET Core metadata to generate an OpenAPI-compatible document.<br \/>\nSwashbuckle \u662f\u4e00\u4e2a\u5f00\u6e90\u9879\u76ee\uff0c\u5b83\u4f7f\u7528 ASP.NET Core \u5143\u6570\u636e\u751f\u6210\u4e0e OpenAPI \u517c\u5bb9\u7684\u6587\u6863\u3002<\/p>\n<\/li>\n<li>\n<p>NSwag is an open-source project that takes an OpenAPI-compatible document and generates clients (and has many other features!).<br \/>\nNSwag \u662f\u4e00\u4e2a\u5f00\u6e90\u9879\u76ee\uff0c\u5b83\u91c7\u7528\u4e0e OpenAPI \u517c\u5bb9\u7684\u6587\u6863\u5e76\u751f\u6210\u5ba2\u6237\u7aef\uff08\u5e76\u4e14\u5177\u6709\u8bb8\u591a\u5176\u4ed6\u529f\u80fd\uff01\uff09<\/p>\n<\/li>\n<li>\n<p>Swagger UI is an open-source project for interacting with APIs based on the OpenAPI document.<br \/>\nSwagger UI \u662f\u4e00\u4e2a\u5f00\u6e90\u9879\u76ee\uff0c\u7528\u4e8e\u4e0e\u57fa\u4e8e OpenAPI \u6587\u6863\u7684 API \u4ea4\u4e92\u3002<\/p>\n<\/li>\n<\/ul>\n<p>Some of these projects have direct dependencies on others (everything depends on the OpenAPI specification, for example), but they may evolve at difference paces. If Swashbuckle doesn\u2019t support some new feature of the OpenAPI specification, it won\u2019t appear in your documents, and NSwag won\u2019t be able to use it.<\/p>\n<p>\u5176\u4e2d\u4e00\u4e9b\u9879\u76ee\u76f4\u63a5\u4f9d\u8d56\u4e8e\u5176\u4ed6\u9879\u76ee\uff08\u4f8b\u5982\uff0c\u4e00\u5207\u90fd\u53d6\u51b3\u4e8e OpenAPI \u89c4\u8303\uff09\uff0c\u4f46\u5b83\u4eec\u7684\u53d1\u5c55\u901f\u5ea6\u53ef\u80fd\u4e0d\u540c\u3002\u5982\u679c Swashbuckle \u4e0d\u652f\u6301 OpenAPI \u89c4\u8303\u7684\u67d0\u4e9b\u65b0\u529f\u80fd\uff0c\u5b83\u4e0d\u4f1a\u51fa\u73b0\u5728\u4f60\u7684\u6587\u6863\u4e2d\uff0cNSwag \u4e5f\u65e0\u6cd5\u4f7f\u7528\u5b83\u3002<\/p>\n<p>Most of the tools provide ways to override the behavior to work around these rough edges, but the reality is that if you\u2019re using newer or less popular features, you may have more difficulty persuading all the tools in your tool chain to play together nicely.<\/p>\n<p>\u5927\u591a\u6570\u5de5\u5177\u90fd\u63d0\u4f9b\u4e86\u8986\u76d6\u884c\u4e3a\u7684\u65b9\u6cd5\uff0c\u4ee5\u89e3\u51b3\u8fd9\u4e9b\u7c97\u7cd9\u7684\u8fb9\u7f18\uff0c\u4f46\u73b0\u5b9e\u60c5\u51b5\u662f\uff0c\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f\u8f83\u65b0\u6216\u4e0d\u592a\u6d41\u884c\u7684\u529f\u80fd\uff0c\u5219\u8bf4\u670d\u5de5\u5177\u94fe\u4e2d\u7684\u6240\u6709\u5de5\u5177\u5f88\u597d\u5730\u534f\u540c\u5de5\u4f5c\u53ef\u80fd\u4f1a\u66f4\u52a0\u56f0\u96be\u3002<\/p>\n<p>Overall, the important thing to remember is that OpenAPI documents may work well if you have simple requirements or want to use Swagger UI only for testing. In these cases, there\u2019s little investment required to add OpenAPI support, and it can improve your workflow, so you might find it worthwhile to try.<\/p>\n<p>\u603b\u7684\u6765\u8bf4\uff0c\u8981\u8bb0\u4f4f\u7684\u91cd\u8981\u4e00\u70b9\u662f\uff0c\u5982\u679c\u60a8\u6709\u7b80\u5355\u7684\u8981\u6c42\u6216\u53ea\u60f3\u4f7f\u7528 Swagger UI \u8fdb\u884c\u6d4b\u8bd5\uff0cOpenAPI \u6587\u6863\u53ef\u80fd\u4f1a\u5f88\u597d\u5730\u5de5\u4f5c\u3002\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u6dfb\u52a0 OpenAPI \u652f\u6301\u53ea\u9700\u8981\u5f88\u5c11\u7684\u6295\u8d44\uff0c\u800c\u4e14\u5b83\u53ef\u4ee5\u6539\u5584\u60a8\u7684\u5de5\u4f5c\u6d41\u7a0b\uff0c\u56e0\u6b64\u60a8\u53ef\u80fd\u4f1a\u53d1\u73b0\u503c\u5f97\u5c1d\u8bd5\u3002<\/p>\n<p>If you have more complex requirements, are creating an API that OpenAPI can\u2019t easily describe or aren\u2019t a fan of the code generation, it may not be worth your time to invest heavily in OpenAPI for your documents.<\/p>\n<p>\u5982\u679c\u60a8\u6709\u66f4\u590d\u6742\u7684\u8981\u6c42\uff0c\u6b63\u5728\u521b\u5efa OpenAPI \u65e0\u6cd5\u8f7b\u677e\u63cf\u8ff0\u7684 API\uff0c\u6216\u8005\u4e0d\u559c\u6b22\u4ee3\u7801\u751f\u6210\uff0c\u90a3\u4e48\u53ef\u80fd\u4e0d\u503c\u5f97\u60a8\u82b1\u65f6\u95f4\u4e3a\u60a8\u7684\u6587\u6863\u6295\u5165\u5927\u91cf OpenAPI\u3002<\/p>\n<p><strong>Tip<\/strong> If you\u2019re a fan of code generation but prefer more of a remote procedure call (RPC) style of programming, it\u2019s worthwhile to look at gRPC. Code generation for gRPC is robust, supported across multiple languages, and has great support in .NET. You can read more in the documentation at <a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/grpc\">https:\/\/learn.microsoft.com\/aspnet\/core\/grpc<\/a>.<br \/>\n<strong>\u63d0\u793a<\/strong> \u5982\u679c\u60a8\u559c\u6b22\u4ee3\u7801\u751f\u6210\uff0c\u4f46\u66f4\u559c\u6b22\u8fdc\u7a0b\u8fc7\u7a0b\u8c03\u7528 \uff08RPC\uff09 \u98ce\u683c\u7684\u7f16\u7a0b\uff0c\u90a3\u4e48\u503c\u5f97\u8003\u8651\u4e00\u4e0b gRPC\u3002gRPC \u7684\u4ee3\u7801\u751f\u6210\u975e\u5e38\u5065\u58ee\uff0c\u652f\u6301\u591a\u79cd\u8bed\u8a00\uff0c\u5e76\u60a8\u53ef\u4ee5\u5728 <a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/grpc\">https:\/\/learn.microsoft.com\/aspnet\/core\/grpc<\/a> \u4e0a\u7684\u6587\u6863\u4e2d\u9605\u8bfb\u66f4\u591a\u5185\u5bb9\u3002<\/p>\n<p>In chapter 12 we\u2019ll take a brief look at the new object-relational mapper that fits well with ASP.NET Core: Entity Framework Core. You\u2019ll get only a taste of it in this book, but you\u2019ll learn how to load and save data, build a database from your code, and migrate the database as your code evolves.<\/p>\n<p>\u5728\u7b2c 12 \u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u7b80\u8981\u4ecb\u7ecd\u975e\u5e38\u9002\u5408 ASP.NET Core \u7684\u65b0\u5bf9\u8c61\u5173\u7cfb\u6620\u5c04\u5668\uff1aEntity Framework Core\u3002\u5728\u672c\u4e66\u4e2d\uff0c\u60a8\u53ea\u4f1a\u5bf9\u5b83\u6709\u6240\u4e86\u89e3\uff0c\u4f46\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u52a0\u8f7d\u548c\u4fdd\u5b58\u6570\u636e\u3001\u4ece\u4ee3\u7801\u6784\u5efa\u6570\u636e\u5e93\uff0c\u4ee5\u53ca\u968f\u7740\u4ee3\u7801\u7684\u53d1\u5c55\u8fc1\u79fb\u6570\u636e\u5e93\u3002<\/p>\n<h2>11.7 Summary<\/h2>\n<h2>11.7 \u603b\u7ed3<\/h2>\n<p>OpenAPI is a specification for describing HTTP APIs in a machine-readable format, as a JSON document. You can use this document to drive other tooling, such as code generators or API testers.<br \/>\nOpenAPI \u662f\u4e00\u79cd\u89c4\u8303\uff0c\u7528\u4e8e\u4ee5\u673a\u5668\u53ef\u8bfb\u683c\u5f0f\uff08\u5982 JSON \u6587\u6863\uff09\u63cf\u8ff0 HTTP API\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u672c\u6587\u6863\u6765\u9a71\u52a8\u5176\u4ed6\u5de5\u5177\uff0c\u4f8b\u5982\u4ee3\u7801\u751f\u6210\u5668\u6216 API \u6d4b\u8bd5\u5668\u3002<\/p>\n<p>You can add OpenAPI document generation to an ASP.NET Core app by using the NSwag or Swashbuckle NuGet package. These packages work with ASP.NET Core services to read metadata about all the endpoints in your application to build an OpenAPI document.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 NSwag \u6216 Swashbuckle NuGet \u5305\u5c06 OpenAPI \u6587\u6863\u751f\u6210\u6dfb\u52a0\u5230 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u3002\u8fd9\u4e9b\u8f6f\u4ef6\u5305\u4e0e ASP.NET Core \u670d\u52a1\u914d\u5408\u4f7f\u7528\uff0c\u4ee5\u8bfb\u53d6\u6709\u5173\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6240\u6709\u7ec8\u7aef\u8282\u70b9\u7684\u5143\u6570\u636e\uff0c\u4ee5\u6784\u5efa OpenAPI \u6587\u6863\u3002<\/p>\n<p>The Swashbuckle Swagger middleware exposes the OpenAPI Document for your application at the path \/swagger\/v1\/swagger.json by default. Exposing the document in this way makes it easy for other tools to understand the endpoints in your application.<br \/>\nSwashbuckle Swagger \u4e2d\u95f4\u4ef6\u5728\u8def\u5f84\u4e2d\u516c\u5f00\u5e94\u7528\u7a0b\u5e8f\u7684 OpenAPI \u6587\u6863\/swagger\/v1\/swagger.json \u4e2d\u3002\u66b4\u9732\u8be5\u6587\u6863\u4f7f\u5176\u4ed6\u5de5\u5177\u80fd\u591f\u8f7b\u677e\u7406\u89e3\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u7aef\u70b9\u3002<\/p>\n<p>You can explore and test your API by using Swagger UI. The Swashbuckle Swagger UI middleware exposes the UI at the path \/swagger by default. You can use Swagger UI to explore your API, send test requests to your endpoints, and check how well your API is documented.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 Swagger UI \u63a2\u7d22\u548c\u6d4b\u8bd5\u60a8\u7684 API\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cSwashbuckle Swagger UI \u4e2d\u95f4\u4ef6\u5728\u8def\u5f84 \/swagger \u5904\u516c\u5f00 UI\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 Swagger UI \u6765\u6d4f\u89c8 API\uff0c\u5411\u7ec8\u7aef\u8282\u70b9\u53d1\u9001\u6d4b\u8bd5\u8bf7\u6c42\uff0c\u5e76\u68c0\u67e5 API \u7684\u6587\u6863\u8bb0\u5f55\u60c5\u51b5\u3002<\/p>\n<p>You can customize the OpenAPI description of your endpoints by adding metadata. You can provide tags, for example, by calling WithTags() on an endpoint and specify that an endpoint returns a type T with a 201 status code using <code>Produces&lt;T&gt;(201)<\/code>. Adding metadata improves your API OpenAPI description, which in turn improves tooling such as Swagger UI.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u6dfb\u52a0\u5143\u6570\u636e\u6765\u81ea\u5b9a\u4e49\u7ec8\u7aef\u8282\u70b9\u7684 OpenAPI \u63cf\u8ff0\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u7ec8\u7aef\u8282\u70b9\u4e0a\u8c03\u7528 WithTags\uff08\uff09 \u6765\u63d0\u4f9b\u6807\u7b7e\uff0c\u5e76\u4f7f\u7528 <code>Produces&lt;T&gt;\uff08201\uff09<\/code> \u6307\u5b9a\u7ec8\u7aef\u8282\u70b9\u8fd4\u56de\u72b6\u6001\u4ee3\u7801\u4e3a 201 \u7684\u7c7b\u578b T\u3002\u6dfb\u52a0\u5143\u6570\u636e\u53ef\u4ee5\u6539\u8fdb API \u7684 OpenAPI \u63cf\u8ff0\uff0c\u4ece\u800c\u6539\u8fdb Swagger UI \u7b49\u5de5\u5177\u3002<\/p>\n<p>You can use NSwag to generate a C# client from an OpenAPI description. This approach takes care of using the correct paths to call the API, substituting parameters in the path, and serializing and deserializing requests to the API, removing much of the boilerplate associated with interacting with an API.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 NSwag \u4ece OpenAPI \u63cf\u8ff0\u751f\u6210 C# \u5ba2\u6237\u7aef\u3002\u6b64\u65b9\u6cd5\u8d1f\u8d23\u4f7f\u7528\u6b63\u786e\u7684\u8def\u5f84\u8c03\u7528 API\u3001\u66ff\u6362\u8def\u5f84\u4e2d\u7684\u53c2\u6570\u4ee5\u53ca\u5e8f\u5217\u5316\u548c\u53cd\u5e8f\u5217\u5316\u5bf9 API \u7684\u8bf7\u6c42\uff0c\u4ece\u800c\u5220\u9664\u4e0e\u4e0e API \u4ea4\u4e92\u76f8\u5173\u7684\u5927\u90e8\u5206\u6837\u677f\u3002<\/p>\n<p>You can add code generation to your project by using Visual Studio or the .NET API tool or by making manual changes to your project. Visual Studio and the .NET tool automate downloading the OpenAPI description to your local project and adding the necessary NuGet packages. You should update the NuGet packages to the latest versions to ensure that you have the latest bug or security fixes.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 Visual Studio \u6216 .NET API \u5de5\u5177\uff0c\u6216\u8005\u901a\u8fc7\u624b\u52a8\u66f4\u6539\u9879\u76ee\u6765\u5411\u9879\u76ee\u6dfb\u52a0\u4ee3\u7801\u751f\u6210\u3002Visual Studio \u548c .NET \u5de5\u5177\u4f1a\u81ea\u52a8\u5c06 OpenAPI \u63cf\u8ff0\u4e0b\u8f7d\u5230\u672c\u5730\u9879\u76ee\uff0c\u5e76\u6dfb\u52a0\u5fc5\u8981\u7684 NuGet \u5305\u3002\u5e94\u5c06 NuGet \u5305\u66f4\u65b0\u5230\u6700\u65b0\u7248\u672c\uff0c\u4ee5\u786e\u4fdd\u60a8\u62e5\u6709\u6700\u65b0\u7684 bug \u6216\u5b89\u5168\u4fee\u590d\u3002<\/p>\n<p>NSwag automatically generates a C# method name on the main client class for each endpoint in the OpenAPI description. If the endpoint\u2019s OperationID is missing, NSwag generates a name, which may not be optimal. You can specify the OperationID to use for an endpoint in your OpenAPI description by calling WithName() on the endpoint.<br \/>\nNSwag \u5728 OpenAPI \u63cf\u8ff0\u4e2d\u7684\u6bcf\u4e2a\u7aef\u70b9\u7684\u4e3b\u5ba2\u6237\u7aef\u7c7b\u4e0a\u81ea\u52a8\u751f\u6210\u4e00\u4e2a C# \u65b9\u6cd5\u540d\u79f0\u3002\u5982\u679c\u7f3a\u5c11\u7ec8\u7ed3\u70b9\u7684 OperationID\uff0cNSwag \u4f1a\u751f\u6210\u4e00\u4e2a\u540d\u79f0\uff0c\u8fd9\u53ef\u80fd\u4e0d\u662f\u6700\u4f73\u540d\u79f0\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u7ec8\u7aef\u8282\u70b9\u4e0a\u8c03\u7528 WithName\uff08\uff09 \u6765\u6307\u5b9a OpenAPI \u63cf\u8ff0\u4e2d\u8981\u7528\u4e8e\u7ec8\u7aef\u8282\u70b9\u7684 OperationID\u3002<\/p>\n<p>You can customize the client NSwag generates by adding an <code>&lt;Options&gt;<\/code> element inside the <code>&lt;OpenApiReference&gt;<\/code> in your .csproj file. These options are specified as command-line switches such as \/JsonLibrary:SystemTextJson. You can change many things about the generated code with these switches, such as the serialization library to use and whether to generate an interface for the client.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728<code>&lt;OpenApiReference&gt;<\/code> \u5728 .csproj \u6587\u4ef6\u4e2d\u3002\u8fd9\u4e9b\u9009\u9879\u6307\u5b9a\u4e3a\u547d\u4ee4\u884c\u5f00\u5173\uff0c\u4f8b\u5982 \/JsonLibrary\uff1aSystemTextJson\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e9b\u5f00\u5173\u66f4\u6539\u6709\u5173\u751f\u6210\u4ee3\u7801\u7684\u8bb8\u591a\u5185\u5bb9\uff0c\u4f8b\u5982\u8981\u4f7f\u7528\u7684\u5e8f\u5217\u5316\u5e93\u4ee5\u53ca\u662f\u5426\u4e3a Client \u7aef\u751f\u6210\u63a5\u53e3\u3002<\/p>\n<p>If the OpenAPI description for a remote API changes, you need to download the document to your project again for the generated client to reflect these changes. If you originally added the OpenAPI reference by using Visual Studio, you should use Visual Studio to refresh the document, and the same applies to the .NET API tool. NSwag automatically updates the generated code when the downloaded OpenAPI document changes.<br \/>\n\u5982\u679c\u8fdc\u7a0b API \u7684 OpenAPI \u63cf\u8ff0\u53d1\u751f\u66f4\u6539\uff0c\u60a8\u9700\u8981\u518d\u6b21\u5c06\u6587\u6863\u4e0b\u8f7d\u5230\u60a8\u7684\u9879\u76ee\u4e2d\uff0c\u4ee5\u4fbf\u751f\u6210\u7684\u5ba2\u6237\u7aef\u53cd\u6620\u8fd9\u4e9b\u66f4\u6539\u3002\u5982\u679c\u6700\u521d\u4f7f\u7528 Visual Studio \u6dfb\u52a0\u4e86 OpenAPI \u5f15\u7528\uff0c\u5219\u5e94\u4f7f\u7528 Visual Studio \u5237\u65b0\u6587\u6863\uff0c\u8fd9\u540c\u6837\u9002\u7528\u4e8e .NET API \u5de5\u5177\u3002\u5f53\u4e0b\u8f7d\u7684 OpenAPI \u6587\u6863\u53d1\u751f\u66f4\u6539\u65f6\uff0cNSwag \u4f1a\u81ea\u52a8\u66f4\u65b0\u751f\u6210\u7684\u4ee3\u7801\u3002<\/p>\n<p>You can add an OpenAPI summary and description to an endpoint by installing the Microsoft.AspNetCore.OpenApi package, calling WithOpenApi() on the endpoint, and adding calls to WithSummary() or WithDescription(). This metadata is shown in Swagger UI, and NSwag uses the summary to generate documentation comments in the C# client.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5b89\u88c5 Microsoft.AspNetCore.OpenApi \u5305\uff0c\u5728\u7aef\u70b9\u4e0a\u8c03\u7528 WithOpenApi\uff08\uff09\uff0c\u7136\u540e\u6dfb\u52a0\u5bf9 WithSummary\uff08\uff09 \u6216 WithDescription\uff08\uff09 \u7684\u8c03\u7528\uff0c\u5c06 OpenAPI \u6458\u8981\u548c\u8bf4\u660e\u6dfb\u52a0\u5230\u7aef\u70b9\u3002\u6b64\u5143\u6570\u636e\u663e\u793a\u5728 Swagger UI \u4e2d\uff0cNSwag \u4f7f\u7528\u6458\u8981\u5728 C# \u5ba2\u6237\u7aef\u4e2d\u751f\u6210\u6587\u6863\u6ce8\u91ca\u3002<\/p>\n<p>You can use attributes instead of fluent methods to add OpenAPI metadata if you prefer. This approach sometimes helps improve readability of your endpoints. You must still call WithOpenApi() on the endpoint to read the metadata attributes.<br \/>\n\u5982\u679c\u9700\u8981\uff0c\u53ef\u4ee5\u4f7f\u7528\u5c5e\u6027\u800c\u4e0d\u662f Fluent \u65b9\u6cd5\u6765\u6dfb\u52a0 OpenAPI \u5143\u6570\u636e\u3002\u6b64\u65b9\u6cd5\u6709\u65f6\u6709\u52a9\u4e8e\u63d0\u9ad8\u7ec8\u7aef\u8282\u70b9\u7684\u53ef\u8bfb\u6027\u3002\u60a8\u4ecd\u5fc5\u987b\u5728\u7ec8\u7aef\u8282\u70b9\u4e0a\u8c03\u7528 WithOpenApi\uff08\uff09 \u624d\u80fd\u8bfb\u53d6\u5143\u6570\u636e\u5c5e\u6027\u3002<\/p>\n<p>You can use XML documentation comments to document your OpenAPIs to reduce the clutter of extra method calls and attributes. To use this approach, you must enable documentation generation for the project, configure Swashbuckle to read the XML documentation file on startup, and use static or instance handler methods instead of lambda methods.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 XML \u6587\u6863\u6ce8\u91ca\u6765\u8bb0\u5f55 OpenAPI\uff0c\u4ee5\u51cf\u5c11\u989d\u5916\u65b9\u6cd5\u8c03\u7528\u548c\u5c5e\u6027\u7684\u6df7\u4e71\u3002\u8981\u4f7f\u7528\u6b64\u65b9\u6cd5\uff0c\u60a8\u5fc5\u987b\u4e3a\u9879\u76ee\u542f\u7528\u6587\u6863\u751f\u6210\uff0c\u5c06 Swashbuckle \u914d\u7f6e\u4e3a\u5728\u542f\u52a8\u65f6\u8bfb\u53d6 XML \u6587\u6863\u6587\u4ef6\uff0c\u5e76\u4f7f\u7528\u9759\u6001\u6216\u5b9e\u4f8b\u5904\u7406\u7a0b\u5e8f\u65b9\u6cd5\u800c\u4e0d\u662f lambda \u65b9\u6cd5\u3002<\/p>\n<p>Not all APIs can be described by the OpenAPI specification. Some styles, such as HATEOAS, are naturally dynamic and don\u2019t lend themselves to the static design of OpenAPI. You may also have difficulty with complex authentication requirements, as well as combining OpenAPI documents. In these cases, you may find that OpenAPI brings little value to your application.<br \/>\n\u5e76\u975e\u6240\u6709 API \u90fd\u53ef\u4ee5\u7528 OpenAPI \u89c4\u8303\u6765\u63cf\u8ff0\u3002\u67d0\u4e9b\u6837\u5f0f\uff08\u5982 HATEOAS\uff09\u81ea\u7136\u662f\u52a8\u6001\u7684\uff0c\u5e76\u4e0d\u9002\u5408 OpenAPI \u7684\u9759\u6001\u8bbe\u8ba1\u3002\u60a8\u53ef\u80fd\u8fd8\u96be\u4ee5\u6ee1\u8db3\u590d\u6742\u7684\u8eab\u4efd\u9a8c\u8bc1\u8981\u6c42\u4ee5\u53ca\u7ec4\u5408 OpenAPI \u6587\u6863\u3002\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u60a8\u53ef\u80fd\u4f1a\u53d1\u73b0 OpenAPI \u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5e26\u6765\u4ec0\u4e48\u4ef7\u503c\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>11 Documenting APIs with OpenAPI 11 \u4f7f\u7528 OpenAPI \u8bb0\u5f55 API This chapter covers \u672c\u7ae0\u6db5\u76d6 Understanding OpenAPI and seeing why it\u2019s useful \u4e86\u89e3 OpenAPI \u5e76\u4e86\u89e3\u5b83\u4e3a\u4f55\u6709\u7528 Adding an OpenAPI description to your app \u5c06 OpenAPI \u63cf\u8ff0\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f Improving your OpenAPI descriptions by adding metadata to endpoints \u901a\u8fc7\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u5143\u6570\u636e\u6765\u6539\u8fdb OpenAPI \u63cf\u8ff0 Generating a C# client from your OpenAPI description \u4ece [&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":[19],"class_list":["post-593","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-asp-net-core-in-action"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/593","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=593"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/593\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=593"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=593"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=593"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}