整合營銷服務商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          .NET Core WebAPI中使用Swagger

          .NET Core WebAPI中使用Swagger(完整教程)


          、Swagger簡介

          1.1-什么是Swagger?

          • Swagger是一個規(guī)范且完整的框架,用于生成、描述、調(diào)試和可視化Restfull風格的Web服務。
          • Swagger的目標是對Rest API定義一個標準且和語言無關的接口,可以讓人和計算機擁有無需訪問源碼、文檔或網(wǎng)絡流量監(jiān)控就可以發(fā)現(xiàn)和連接服務的能力。當通過Swagger進行正確定義,用于可以理解遠程服務并使用最少邏輯與遠程服務進行交互。與為底層編程所實現(xiàn)的接口類似,Swagger消除了調(diào)用服務時可能會有的猜測。


          1.2-Swagger有什么優(yōu)勢?

          • 支持API自動生成同步的在線文檔:使用Swagger后可以直接通過代碼生成文檔,不需要自己去手動編寫接口文檔了,對程序員來說是非常方便。
          • 提供Web頁面在線測試API:光有文檔還不夠,Swagger生成的文檔還支持在線測試。參數(shù)和格式都一定定義好了,直接在界面上輸出參數(shù)對應的值即可在先測試接口。
          • Swagger可以生成客戶端SDK代碼用于各種不同平臺的實現(xiàn)。
          • Swagger文件可以在許多不同的平臺上從代碼注釋中自動生成。
          • Swagger有一個強大的社區(qū),里面有許多強悍的貢獻者。


          1.3-Swagger、OpenAPI3.0、Restful API的區(qū)別?

          • Open API:OpenAPI是規(guī)范的正式名稱。該規(guī)范的開發(fā)時由OpenAPI Initative推動的。該提倡涉及不同領域的30個組織——包括Microsoft、Google、IBM和CapitalOne.
          • Swagger:實現(xiàn)OpenAPI規(guī)范的工具之一。
          • RestfulAPI:是一種WebAPI設計架構風格。其中Rest即Represntaional State Transfer的縮寫,可以翻譯為"狀態(tài)表述轉(zhuǎn)換"。是目前比較成熟的一套互聯(lián)網(wǎng)應用程序的API設計架構風格OpenAPI規(guī)范即是這個架構風格的具體實現(xiàn)規(guī)范。


          1.4-Swagger工具


          1.5-Swagger UI工具主要功能

          • 接口的文檔在線自動生成
          • 功能測試等


          1.6-Swashbuckle組件

          • Swashbuckle是.NET Core中對Swagger的封裝,他有2個核心組件:Swashbuckle.SwaggerGen:提供生成對象、方法、返回類型等JSON Swagger文檔的功能。Swashbuckle.SwaggerUI:是一個嵌入式版本的SwaggerUI工具,使用Swagger UI強大的富文本表現(xiàn)形式可定制化的來描述Web API的功能,并且包含內(nèi)置的公共方法測試工具。


          1.7-TPL

          • 任務并行庫(TPL)是System.Threading.Tasks命名空間中的一組公共類型和API
          • TPL動態(tài)的擴展并發(fā)度,以最有效的使用可用的處理器。通過使用TPL,您可以最大限度的提高代碼的性能,同時專注于您的代碼業(yè)務。
          • 從.NETFramework4開始,TPL是編寫多線程和并行代碼的首選方式。

          二、在ASP.NET Core Web API中使用Swagger UI

          2.1-創(chuàng)建一個WebAPI項目


          2.2-下載、安裝、引入【Swashbuckle.AspNetCore】包

          右擊【解決方案】,然后點擊【管理Nuget包】,搜索【Swashbuckle.AspNetCore】包,點擊【安裝】即可,博主這里下載的是【最新穩(wěn)定版5.6.3】。


          2.3-配置Swagger中間件(注冊 Swagger 服務)

          在【Startup.cs】文件中的【ConfigureService】類中引入命名空間,并注冊Swagger服務并配置文檔信息。

          //引入命名空間
          using Microsoft.OpenApi.Models;
          
          //注冊Swagger
          services.AddSwaggerGen(u=> {
              u.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
                           {
                               Version="Ver:1.0.0",//版本
                               Title="xxx后臺管理系統(tǒng)",//標題
                               Description="xxx后臺管理系統(tǒng):包括人員信息、團隊管理等。",//描述
                               Contact=new Microsoft.OpenApi.Models.OpenApiContact { 
                                   Name="西瓜程序猿",
                                   Email="xxx@qq.com"
                                   }
                           });
          });
          

          如果安裝的 Swashbuckle.AspNetCore Nuget包版本<=3.0.0,寫法略有不同。將 【OpenApiInfo】 替換成 【Info】 即可。

          services.AddSwaggerGen(x=>
          {
          
              x.SwaggerDoc("v1", new Info() { Title="Web Api", Version="v1" });
          
          });
          


          2.4-啟用Swagger中間件

          在【Startup.cs】文件中的【Configure】類中啟用Swagger中間件,為生成的JSON文檔和SwaggerUI提供服務。

           //啟用Swagger中間件
          app.UseSwagger();
          //配置SwaggerUI
          app.UseSwaggerUI(u=>
                           {
                               u.SwaggerEndpoint("/swagger/v1/swagger.json", "WebAPI_v1");
                           });
          


          2.5-運行項目即可

          2.5.1-如果我們直接運行項目,會出現(xiàn)這樣的界面,說明我們的Web API程序沒有問題。


          2.5.2-然后我們在地址欄中輸入【swagger/v1/swagger.json】。


          可以看到瀏覽器出現(xiàn)這樣的界面,如果出現(xiàn)這樣的界面,說明Swagger也沒有問題。


          注意:按照2.5.1在地址欄中的【swagger/v1/swagger.json】需要與在【Startup.cs】文件中的【Configure】類中啟用Swagger中間件添加的代碼保持一致,因為這段代碼就是自動生成JSON文件的,你配置的路徑和JSON文件地址是什么,就在瀏覽器中輸入對應的即可。


          2.5.3-以上步驟都沒問題的話,然后我們地址欄中輸入【swagger/index.html】。


          如果能出現(xiàn)以下界面,說明SwaggerUI也沒有問題,都全部正常了。


          2.6-如果想每次運行都默認直接到Swagger的頁面

          2.6.1-打開【launchSettings.json】這個文件。


          2.6.2-然后將【launchUrl】的值從【weatherforecast】修改成【swagger】。


          2.6.3-然后運行項目就直接進入Swagger首頁了。


          2.7-描述信息詳細講解


          三、啟用XML注釋

          3.1-雙擊解決方案


          3-2-然后進入這個頁面,加上這個代碼

          <GenerateDocumentationFile>true</GenerateDocumentationFile>
          <NoWarn>$(NoWarn);1591</NoWarn>
          


          3-3.在【Startup.cs】文件中的【ConfigureService】類中注冊讀取XML信息的Swagger。

            #region 讀取xml信息
               // 使用反射獲取xml文件,并構造出文件的路徑
              var xmlFile=$"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
              var xmlPath=Path.Combine(AppContext.BaseDirectory, xmlFile);
              // 啟用xml注釋,該方法第二個參數(shù)啟用控制器的注釋,默認為false.
              u.IncludeXmlComments(xmlPath, true);
            #endregion
          


          注意:

          • 對于Linux或者非Windows操作系統(tǒng),文件名和路徑區(qū)分大小寫。例如“MyWebApiUseSwagger.xml”文件在Windows上有效,但在CentOS上是無效的。
          • 獲取應用程序路徑,建議采用Path.GetDirectoryName(typeof(Program).Assembly.Location)這種方式或者·AppContext.BaseDirectory這樣來獲取


          四、實例

          4.1-寫一個實例:在【W(wǎng)eatherForecastController】控制器中寫一個方法。


          4.2-寫上以下代碼然后進行請求。

          /// <summary>
          /// 這是一個例子
          /// </summary>
          /// <remarks>
          /// 描述:這是一個帶參數(shù)的GET請求
          /// Web API
          /// </remarks>
          /// <param name="id">主鍵ID</param>
          /// <returns>測試字符串</returns>
          [HttpGet("{id}")]
          public ActionResult<string> Get(int id) {
               return $"你請求的ID是:{id}";
          }
          


          4.3-可以看到【XML注釋】起作用了,然后調(diào)用也成功了。


          五、小知識點

          5.1-當入?yún)?出參返回object或者匿名類時,也需要加上注釋怎么辦?

          (1)在方法中加上以下特性:

           [ProducesResponseType(typeof(xxx),200)]
          


          (2)在Remarks節(jié)點下進行注釋:



          5.2-如何隱藏接口:有接口但是不想讓別人看到?

          在需要進行隱藏的接口上加上以下特性即可:

            [ApiExplorerSettings(IgnoreApi=true)]
          


          5.3-設置路由前綴/自定義頭內(nèi)容/網(wǎng)頁標題

          如果不想加上"swagger",而輸入5000即可訪問,也可以自定義路由前綴,加上以下代碼即可。


          5.3-自定義首頁

          (1)新建一個【index.html】,右擊該文件,點擊【屬性】,進行設置相關操作。



          (2)在Startup.cs進行配置。


          (3)靜態(tài)頁面下載地址:

          https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html


          5.4-開啟JWT認證

          (1)在Startup.cs進行配置。

           #region 開啟JWT
             u.OperationFilter<SecurityRequirementsOperationFilter>();
          
          	u.AddSecurityDefinition("oauth2", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
          	{
          		Description="JWT授權(數(shù)據(jù)將在請求頭中進行傳輸)直接在下框中輸入Bearer { token } (注意兩者之間是一個空格) ",
            		Name="Authorization",
          		In=Microsoft.OpenApi.Models.ParameterLocation.Header,//jwt默認存放Authorazation信息的位置(請求頭中)
          		Type=Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey
          	});
          #endregion
          


          (2)下載包,注意版本號問題,盡量保持一致,不然會報錯。


          (3)然后將接口加上權限,去請求該接口,可以看到請求頭中會有以下信息了。


          5.5-使用Cookie持久化存儲Token,解決每次刷新需要重新輸入Token授權

          目錄結構:

          (1)在【index.html】文件導入abp.js/swagger.js文件。


          (2)在【swagger.js】里面需要注意請求授權地址。


          (3)后端授權邏輯。

          小明最近又遇到麻煩了,小紅希望對接接口傳送的數(shù)據(jù)進行驗證,既然是小紅要求,那小明說什么都得滿足呀,這還不簡單嘛。

          傳統(tǒng)驗證

          [HttpPost]
          public async Task<ActionResult<Todo>> PostTodo(Todo todo)
          {
              if (string.IsNullOrEmpty(todo.Name))
              {
                  return Ok("名稱不能為空");
              }
              context.Todo.Add(todo);
              await context.SaveChangesAsync();
          
              return CreatedAtAction("GetTodo", new { id=todo.Id }, todo);
          }
          

          小明寫著寫著發(fā)現(xiàn)這樣寫,很多接口相同得地方都要寫,使得代碼比較臃腫。

          使用模型驗證

          在參數(shù)模型上打上注解

          namespace App001.Models
          {
              /// <summary>
              /// 待辦事項
              /// </summary>
              public class Todo
              {
                  /// <summary>
                  /// ID
                  /// </summary>
                  public Guid Id { get; set; }
                  /// <summary>
                  /// 名稱
                  /// </summary>
                  [Required(ErrorMessage="名稱不能為空")]
                  public string Name { get; set; }
              }
          }
          

          Postman測試Name傳值未空時,則返回:

          {
              "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
              "title": "One or more validation errors occurred.",
              "status": 400,
              "traceId": "|df184e36-4e11844dfd38a626.",
              "errors": {
                  "Name": [
                      "名稱不能為空"
                  ]
              }
          }
          

          注意Web API 控制器具有 [ApiController] 特性,則它們不必檢查ModelState.IsValid。在此情況下,如果模型狀態(tài)無效,將返回包含錯誤詳細信息的自動 HTTP 400 響應。

          內(nèi)置特性

          • [CreditCard]:驗證屬性是否具有信用卡格式。
          • [Compare]:驗證模型中的兩個屬性是否匹配。
          • [EmailAddress]:驗證屬性是否具有電子郵件格式。
          • [Phone]:驗證屬性是否具有電話號碼格式。
          • [Range]:驗證屬性值是否在指定的范圍內(nèi)。
          • [RegularExpression]:驗證屬性值是否與指定的正則表達式匹配。
          • [Required]:驗證字段是否不為 null。
          • [StringLength]:驗證字符串屬性值是否不超過指定長度限制。
          • [Url]:驗證屬性是否具有 URL 格式。
          • [Remote]:通過在服務器上調(diào)用操作方法來驗證客戶端上的輸入。

          Error messages

          通過驗證特性可以指定要為無效輸入顯示的錯誤消息。 例如:

          [Required(ErrorMessage="名稱不能為空")]
          

          使用自定義返回消息格式

          有兩種方式:

          1. 使用自定義過濾器
          2. 使用默認模型驗證,需要在控制器上面加上【ApiController】。

          使用自定義過濾器

          首先,創(chuàng)建ModelValidateActionFilterAttribute過濾器。

          public class ModelValidateActionFilterAttribute : ActionFilterAttribute
          {
              public override void OnActionExecuting(ActionExecutingContext context)
              {
                  if (!context.ModelState.IsValid)
                  {
                      //獲取驗證失敗的模型字段
                      var errors=context.ModelState
                          .Where(e=> e.Value.Errors.Count > 0)
                          .Select(e=> e.Value.Errors.First().ErrorMessage)
                          .ToList();
          
                      var str=string.Join("|", errors);
          
                      //設置返回內(nèi)容
                      var result=new
                      {
                          Code=10000,
                          Msg="未通過數(shù)據(jù)驗證。",
                          FullMsg=str
                      };
          
                      context.Result=new BadRequestObjectResult(result);
                  }
          
              }
          }
          

          然后,Startup.ConfigureServices將過濾器添加到控制器中并關閉默認模型驗證,另外我們還添加了AddNewtonsoftJson。

          //關閉默認模型驗證
          services.Configure<ApiBehaviorOptions>(opt=> opt.SuppressModelStateInvalidFilter=true);
          services.AddControllers(opt=>
          {
              //添加過濾器
              opt.Filters.Add(typeof(ModelValidateActionFilterAttribute));
          }).AddNewtonsoftJson(opt=>
          {
              //json字符串大小寫原樣輸出
              opt.SerializerSettings.ContractResolver=new DefaultContractResolver();
          });
          

          最后,我們看一下返回效果:

          {
              "Code": 10000,
              "Msg": "未通過數(shù)據(jù)驗證。",
              "FullMsg": "名稱不能為空。"
          }
          

          使用默認模型驗證

          services.Configure<ApiBehaviorOptions>(opt=>
          {
              opt.InvalidModelStateResponseFactory=actionContext=>
              {
                  //獲取驗證失敗的模型字段 
                  var errors=actionContext.ModelState
                      .Where(e=> e.Value.Errors.Count > 0)
                      .Select(e=> e.Value.Errors.First().ErrorMessage)
                      .ToList();
          
                  var str=string.Join("|", errors);
          
                  //設置返回內(nèi)容
                  var result=new
                  {
                      Code=10000,
                      Msg="未通過數(shù)據(jù)驗證。",
                      FullMsg=str
                  };
          
                  return new BadRequestObjectResult(result);
              };
          });
          

          小結

          目前為止,小明把數(shù)據(jù)驗證也搞定了,是不是so easy!


          章涵蓋

          • Web API 概述和實際案例示例
          • Web API 的類型及其優(yōu)缺點
          • ASP.NET 核心概述
          • 主要 ASP.NET 核心架構原則

          幾乎所有應用程序都需要數(shù)據(jù),尤其是基于 Web 的應用程序,其中大量客戶端與集中式實體(通常是基于 HTTP 的服務)交互,以訪問信息并可能更新或操作它們。在本書中,我們將學習如何設計和開發(fā)一種特定類型的基于 HTTP 的服務,其唯一目的是向這些客戶端提供數(shù)據(jù),允許它們以統(tǒng)一、結構化和標準化的方式與所需的信息進行交互:Web API

          在本章的第一部分中,我們將了解 Web API 的獨特特征,并了解如何將其應用于多個實際場景。在第二部分中,我們將熟悉 ASP.NET Core,我們將在本書中用于創(chuàng)建Web API的Web框架。

          1.1 網(wǎng)頁接口

          應用程序編程接口API) 是一種軟件接口,它公開計算機程序用于相互交互和交換信息的工具和服務。執(zhí)行此類交換所需的連接是通過通用通信標準(協(xié)議)、給定的可用操作集(規(guī)范)和數(shù)據(jù)交換格式(JSON、XML 等)建立的。

          從這個定義中,我們可以很容易地看到,API 的主要目的是允許各方使用通用語法(或抽象)進行通信,該語法(或抽象)簡化和標準化每個引擎蓋下發(fā)生的事情。整體概念類似于現(xiàn)實世界的接口,后者也提供了一個通用的“語法”,允許不同的各方進行操作。一個完美的例子是插頭插座,這是所有國家電氣系統(tǒng)使用的抽象概念,允許家用電器和電子設備通過給定的電壓、頻率和插頭類型標準與電源交互。

          圖1.1顯示了形成電網(wǎng)的主要組成部分:一個互連的網(wǎng)絡,用于從生產(chǎn)者向住宅消費者發(fā)電、傳輸和輸送電力。正如我們所看到的,每個組件都以不同的方式處理電力,并使用各種“協(xié)議”和“適配器”(電纜線、變壓器等)與其他組件進行通信,最終目標是將其帶到人們的家中。當住宅單元連接到電網(wǎng)時,家用電器(電視機、烤箱、冰箱等)可以通過安全、受保護、易于使用的接口(交流電源插頭)使用電力。如果我們考慮這些插座的工作原理,我們可以很容易地理解它們在多大程度上簡化了底層電網(wǎng)的技術方面。家用電器不必知道這樣的系統(tǒng)是如何工作的,只要它們能夠處理接口。


          圖1.1 接口的常見示例:交流電源插頭插座

          Web API 與引入萬維網(wǎng)的概念相同:可通過 Web 訪問的接口,該接口公開一個或多個插頭(端點),其他各方(客戶端)可以使用這些接口通過通用通信協(xié)議 (HTTP) 和標準(JSON/XML 格式)與電源(數(shù)據(jù))進行交互。

          注意在本書中,術語Web API將互換使用,以表示接口和實際的Web應用程序。

          1.1.1 概述

          圖 1.2 說明了典型面向服務的體系結構 (SOA) 環(huán)境中 Web API 的用途。SOA 是一種圍繞通過網(wǎng)絡進行通信的各種獨立服務的責任分離而構建的架構風格。


          圖 1.2 Web API 在典型的基于 SOA 的環(huán)境中的角色

          如我們所見,Web API 起著關鍵作用,因為它負責從底層數(shù)據(jù)庫管理系統(tǒng) (DBMS) 中檢索數(shù)據(jù)并使其可用于服務:

          • Web 應用程序 1(例如 React 信息網(wǎng)站),它從 Web API 獲取數(shù)據(jù),通過 HTML 頁面和組件向最終用戶顯示數(shù)據(jù)
          • 移動應用 1(例如 Android 應用)和移動應用 2(可以是同一應用的 iOS 端口),它們還會提取數(shù)據(jù)并通過其本機用戶界面 (UI) 向最終用戶顯示數(shù)據(jù)
          • Web 應用程序 2(例如 PHP 管理網(wǎng)站),它訪問 Web API 以允許管理員與數(shù)據(jù)交互并可能修改數(shù)據(jù)
          • 云服務 1(例如數(shù)據(jù)倉庫),它定期從 Web API 中提取數(shù)據(jù)以饋送其內(nèi)部存儲庫(例如,保留一些訪問日志)
          • 云服務 2(例如機器學習軟件),它定期從 Web API 檢索一些測試數(shù)據(jù),并使用它來執(zhí)行預測并提供有用的見解

          我們剛剛描述的是一個有趣的場景,可以幫助我們理解 Web API 在一個相當常見的基于服務的生態(tài)系統(tǒng)中的角色。但我們?nèi)栽谟懻撘环N非個人的理論方法。讓我們看看如何使相同的體系結構適應特定的現(xiàn)實場景。

          1.1.2 真實世界的例子

          在本節(jié)中,我們將圖 1.1 中描述的抽象概念實例化為具體、可信和現(xiàn)實的場景。每個人都知道什么是棋盤游戲,對吧?我們談論的是帶有骰子、紙牌、棋子和類似東西的桌面游戲。假設我們在棋盤游戲俱樂部的 IT 部門工作。俱樂部有一個棋盤游戲數(shù)據(jù)庫,其中包括游戲信息,例如姓名、出版年份、最小最大玩家、游戲時間、最低年齡、復雜性和機制,以及俱樂部成員、客人和其他玩家隨時間給出的一些排名統(tǒng)計數(shù)據(jù)(評級數(shù)量和平均評級)。我們還假設俱樂部希望使用此數(shù)據(jù)庫來提供一些基于 Web 的服務和應用程序,例如:

          • 最終用戶網(wǎng)站 - 每個人都可以訪問,以展示棋盤游戲及其評級,并為注冊用戶提供一些附加功能(例如創(chuàng)建列表的功能)
          • 移動應用程序 - 也可公開訪問,具有與網(wǎng)站相同的功能,以及適用于智能手機和平板電腦的優(yōu)化 UI/UX(用戶體驗)界面
          • 管理門戶 - 可用于受限制的授權用戶列表,允許這些用戶在數(shù)據(jù)庫中執(zhí)行創(chuàng)建、讀取、更新和刪除 (CRUD) 操作,并執(zhí)行其他基于管理的任務
          • 數(shù)據(jù)分析服務 - 托管在第三方平臺上

          正如我們很容易猜到的那樣,滿足這些要求的一個好方法是實現(xiàn)一個專用的 Web API。這種方法允許我們向所有這些服務提供,而無需將數(shù)據(jù)庫服務器及其底層數(shù)據(jù)模型暴露給其中任何一個。圖 1.3 顯示了體系結構。


          圖 1.3 由單個 MyBGList Web API 提供的多個與棋盤游戲相關的 Web 應用程序和服務

          同樣,Web API 是組織者,獲取包含所有相關數(shù)據(jù)的數(shù)據(jù)源(MyBGList DBMS)并將其提供給其他服務:

          • MyBGList網(wǎng)站 - 完全托管在內(nèi)容交付網(wǎng)絡上的ReactJS網(wǎng)站(遵循Jamstack方法),用戶可以在其中瀏覽棋盤游戲目錄并將游戲添加到一組預定義的列表(擁有,想要嘗試,想要購買等),以及添加自己的自定義列表
          • MyBGList移動應用程序 - 適用于Android和iOS的React Native應用程序,允許用戶執(zhí)行與MyBGList網(wǎng)站上相同的操作(瀏覽并添加到列表)
          • MyBGList 管理門戶 - 托管在安全私有云中的專用虛擬機 (VM) 服務器上的 ASP.NET Web 應用程序,系統(tǒng)管理員可以訪問該應用程序以添加、更新和刪除棋盤游戲,以及執(zhí)行基于維護的任務
          • MyBGList 見解 — 一種軟件即服務 (SaaS) 服務,可定期從 Web API 中提取數(shù)據(jù),以饋送其內(nèi)部存儲庫并執(zhí)行日志記錄、監(jiān)控、性能分析和商業(yè)智能任務

          此 Web API 是我們將在以下章節(jié)中使用的 API。

          什么是 Jamstack?

          Jamstack(JavaScript,API和Markup)是一種現(xiàn)代架構模式,基于將網(wǎng)站預呈現(xiàn)為靜態(tài)HTML頁面并使用JavaScript和Web API加載內(nèi)容。有關 Jamstack 方法的更多信息,請參閱 https://jamstack.org。有關使用 Jamstack 方法開發(fā)基于標準的靜態(tài)網(wǎng)站的綜合指南,請查看 Raymond Camden 和 Brian Rinaldi (https://www.manning.com/books/the-jamstack-book) 的 The Jamstack Book: Beyond Static Sites with JavaScript, API and Markup

          1.1.3 網(wǎng)頁接口的類型

          現(xiàn)在我們已經(jīng)了解了大致情況,我們可以花一些時間探索當今 Web API 開發(fā)人員可用的各種體系結構和消息傳遞協(xié)議。正如我們將能夠看到的,每種類型都有特點、優(yōu)缺點,使其適用于不同的應用程序和業(yè)務。

          你在這本書中找不到的東西

          出于篇幅的原因,我有意將本書的主題限制在HTTP Web API,跳過其他應用層協(xié)議,如高級消息隊列協(xié)議(AMQP)。如果您有興趣通過AMQP了解有關基于消息的應用程序的更多信息,我建議您閱讀Gavin M. Roy(https://www.manning.com/books/rabbitmq-in-depth)的RabbitMQ in Depth

          在查看各種體系結構之前,讓我們簡要總結每個 Web API 通常屬于的四個主要使用范圍:

          • 公共 API - 公共 API 也稱為開放 API(不要與 OpenAPI 規(guī)范混淆)。顧名思義,該術語是指可供任何第三方使用的 API,通常沒有訪問限制。這些 API 通常不涉及身份驗證和授權(如果它們可以免費使用),或者如果出于各種原因(例如應用每次調(diào)用成本)需要識別調(diào)用方,則它們采用密鑰或令牌身份驗證機制。此外,由于它們的端點通常可以從萬維網(wǎng)訪問,因此公共 API 通常使用各種限制、隊列和安全技術來避免被大量并發(fā)請求以及拒絕服務 (DoS) 攻擊所削弱。
          • 合作伙伴 API - 屬于此類別的 API 僅供專門選擇和授權的合作伙伴使用,例如外部開發(fā)人員、系統(tǒng)集成商公司、列入白名單的外部互聯(lián)網(wǎng)協(xié)議 (IP) 等。合作伙伴 API 主要用于促進企業(yè)對企業(yè) (B2B) 活動。例如,一個電子商務網(wǎng)站希望與需要為其自己的客戶關系管理 (CRM) 和營銷自動化系統(tǒng)提供服務的第三方業(yè)務合作伙伴共享其客戶數(shù)據(jù)。這些 API 通常實現(xiàn)強身份驗證機制和 IP 限制技術,以防止未經(jīng)授權的用戶訪問它們;它們絕對不適合最終用戶或“公共”客戶端(如標準網(wǎng)站)使用。
          • 內(nèi)部 API - 也稱為私有 API,這些 API 僅供內(nèi)部使用,例如連接同一組織擁有的不同服務,并且通常托管在同一虛擬專用網(wǎng)絡、Web 場或私有云中。例如,內(nèi)部企業(yè)資源規(guī)劃軟件可以使用內(nèi)部 API 從各種內(nèi)部業(yè)務源(工資單、資產(chǎn)管理、項目管理、采購等)檢索數(shù)據(jù),以及創(chuàng)建高級報告和數(shù)據(jù)可視化。由于這些 API 不向外部公開,因此它們通常實施溫和的身份驗證技術,具體取決于組織的內(nèi)部安全模型及其性能和負載平衡調(diào)整。
          • 復合 API — 有時稱為 API 網(wǎng)關,這些 API 組合多個 API,通過單個調(diào)用執(zhí)行一系列相關或相互依賴的操作。簡而言之,它們允許開發(fā)人員同時訪問多個端點。這種方法主要用于微服務架構模式,其中執(zhí)行復雜任務可能需要以同步或異步方式完成由多個服務處理的子任務鏈。復合 API 主要充當 API 業(yè)務流程協(xié)調(diào)程序,確保執(zhí)行主調(diào)用所需的各種子任務成功并能夠返回有效結果(如果不是,則使整個過程無效)。常見的使用場景可確保多個第三方服務完成各自的工作,例如,當我們需要永久刪除內(nèi)部數(shù)據(jù)庫多個外部數(shù)據(jù)源中的用戶記錄(及其所有個人信息)時,而不會產(chǎn)生原子性問題,例如將潛在的敏感數(shù)據(jù)留在某處。由于編排調(diào)用可以是多種類型(公共、合作伙伴和/或內(nèi)部),因此復合 API 通常最終是混合的,這就是為什么它們通常被視為代表不同的 API 類型。

          1.1.4 架構和消息協(xié)議

          為了履行其在各方之間實現(xiàn)數(shù)據(jù)交換的角色,Web API 需要定義一組清晰、明確的規(guī)則、約束和格式。本書介紹了四種最常用的Web API架構風格和消息協(xié)議:REST、RPC、SOAP 和 GraphQL。每種方法都有獨特的特征、權衡和支持的數(shù)據(jù)交換格式,使其可用于不同的目的。

          休息

          具象狀態(tài)傳輸REST) 是一種體系結構樣式,專為基于網(wǎng)絡的應用程序設計,這些應用程序使用標準 HTTP GET、POST、PATCH、PUT 和 DELETE 請求方法(最初在現(xiàn)已取代的 RFC 2616 https://www.w3.org/Protocols/rfc2616/rfc2616.xhtml 中定義)來訪問和操作數(shù)據(jù)。更具體地說,GET 用于讀取數(shù)據(jù),POST 用于創(chuàng)建新資源或執(zhí)行狀態(tài)更改,PATCH 用于更新現(xiàn)有資源,PUT 用于用新資源替換現(xiàn)有資源(如果不存在則創(chuàng)建它),DELETE 用于永久擦除資源。這種辦法不需要額外的公約來允許當事人進行通信,這使得它易于采用和快速實施。

          然而,REST遠不止于此。它的體系結構范例依賴于六個指導約束,如果正確實現(xiàn),可以極大地使多個 Web API 屬性受益:

          • 客戶端-服務器方法 - RESTful API 應通過將 UI 和數(shù)據(jù)存儲問題分開來強制實施關注點分離原則。此方法特別適用于萬維網(wǎng),其中客戶端(如瀏覽器)具有與 Web 應用程序不同的角色,并且不知道它們是如何實現(xiàn)的。重要的是要明白,分離這些問題不僅可以提高它們的可移植性,還可以使它們更易于實現(xiàn)和維護,從而提高整個系統(tǒng)的簡單性、可伸縮性和可修改性
          • 無狀態(tài) - 服務器應處理客戶端之間的所有通信,而不保留、存儲或保留以前調(diào)用的數(shù)據(jù)(如會話信息)。此外,客戶端應在每次調(diào)用中包含任何與上下文相關的信息,例如身份驗證密鑰。此方法可減少服務器上每個請求的開銷,這可以顯著提高 Web API 的性能可伸縮性屬性,尤其是在負載較重的情況下。
          • 可緩存性 - 客戶端和服務器之間交換的數(shù)據(jù)應使用 HTTP 協(xié)議提供的緩存功能。更具體地說,所有 HTTP 響應都必須在其標頭中包含適當?shù)木彺妫ɑ蚍蔷彺妫┬畔ⅲ詢?yōu)化客戶端工作負載,同時防止它們錯誤地提供過時或過時的內(nèi)容。良好的緩存策略會對大多數(shù) Web API 的可伸縮性和性能屬性產(chǎn)生巨大影響。
          • 分層系統(tǒng) - 將服務器置于一個或多個中間 HTTP 服務或篩選器(如轉(zhuǎn)發(fā)器、反向代理和負載均衡器)后面,可以極大地改善 Web API 的整體安全性方面,以及其性能和可伸縮性屬性。
          • 按需代碼COD) - COD 是唯一可選的 REST 約束。它允許服務器提供客戶端可用于采用自定義行為的可執(zhí)行代碼或腳本。COD 的示例包括分布式計算和遠程評估技術,其中服務器將其部分作業(yè)委托給客戶端,或者需要它們使用復雜或自定義任務在本地執(zhí)行某些檢查,例如驗證是否安裝了某些應用程序或驅(qū)動程序。從更廣泛的意義上講,這種約束還指從服務器獲取構建和加載應用程序所需的源代碼的能力(在REST的早期很少見,但在最近的JavaScript驅(qū)動的Web應用程序中很常見)以及進一步的REST調(diào)用問題。COD 可以提高 Web API 的性能和可伸縮性,但同時,它會降低整體可見性并帶來不平凡的安全風險,這就是它被標記為可選的原因。
          • 統(tǒng)一接口 - 最后一個 REST 約束是最重要的一個。它定義了 RESTful 接口必須具備的四個基本功能,以使客戶端能夠在服務器不知道它們?nèi)绾喂ぷ鞯那闆r下與服務器通信,從而使它們與 Web API 的基礎實現(xiàn)分離。這些功能是
            • 資源標識 - 每個資源必須通過其專用的唯一通用資源標識符 (URI) 統(tǒng)一標識。例如,URI https://mybglist.com/api/games/11 標識單個棋盤游戲。
            • 通過表示操作資源 - 客戶端必須能夠使用資源 URI 和相應的 HTTP 方法對資源執(zhí)行基本操作,而無需其他信息。例如,要讀取棋盤游戲 11 的數(shù)據(jù),客戶端應該向 https://mybglist.com/api/games/11 發(fā)出 GET 請求。如果客戶端想要刪除該數(shù)據(jù),它應該向同一 URI 發(fā)出 DELETE 請求,依此類推。
            • 自描述性郵件 - 每個發(fā)件人的郵件必須包含收件人正確理解和處理郵件所需的所有信息。此要求對客戶端和服務器有效,并且很容易通過 HTTP 協(xié)議實現(xiàn)。請求和響應都旨在包含一組描述協(xié)議版本、方法、內(nèi)容類型、默認語言和其他相關元數(shù)據(jù)的 HTTP 標頭。Web API 和連接客戶端只需確保正確設置標頭。
            • HATEOAS超媒體作為應用程序狀態(tài)的引擎)- 服務器應向客戶端提供有用的信息,但只能通過超鏈接和 URI(或 URI 模板)。此要求將服務器與其客戶端分離,因為除了對超媒體的一般理解之外,客戶端幾乎不需要知道如何與服務器交互。此外,服務器功能可以獨立發(fā)展,而不會產(chǎn)生向后兼容性問題。

          實現(xiàn)所有這些約束的 Web API 可以描述為 RESTful

          介紹 Roy Fielding,REST 無可爭議的父親

          Roy Fielding描述了六個REST約束,他是HTTP規(guī)范的主要作者之一,被廣泛認為是REST之父,在他的論文Architectural Styles and the Design of Network-based Software Architectures中,他在2000年獲得了計算機科學博士學位。菲爾丁論文的原始文本可在 http://mng.bz/19ln 的加利福尼亞大學出版物檔案中找到。

          廣泛接受的規(guī)則和指南,HTTP協(xié)議經(jīng)過驗證的可靠性以及實現(xiàn)和開發(fā)的整體簡單性使REST成為當今最流行的Web API架構。出于這個原因,我們將在本書中開發(fā)的大多數(shù) Web API 項目都使用 REST 架構風格,并遵循 RESTful 方法以及本節(jié)中介紹的六個約束。

          SOAP

          簡單對象訪問協(xié)議SOAP) 是一種消息傳遞協(xié)議規(guī)范,用于通過可擴展標記語言 (XML) 跨網(wǎng)絡交換結構化信息。大多數(shù) SOAP Web 服務都是通過使用 HTTP 進行消息協(xié)商和傳輸來實現(xiàn)的,但也可以使用其他應用層協(xié)議,例如簡單郵件傳輸協(xié)議 (SMTP)。

          SOAP規(guī)范由Microsoft于1999年24月發(fā)布,但直到2003年3月1日才成為Web標準,當時它最終獲得了W2C推薦狀態(tài)(SOAP v3.12,https://www.w<>.org/TR/soap<>)。接受的規(guī)范定義了 SOAP 消息傳遞框架,該框架由以下內(nèi)容組成:

          • SOAP 處理模型 - 定義處理 SOAP 消息的規(guī)則
          • SOAP 擴展性模型 — 介紹 SOAP 功能和 SOAP 模塊
          • SOAP 基礎協(xié)議綁定框架 - 描述使用基礎協(xié)議創(chuàng)建綁定的規(guī)則,該協(xié)議可用于在 SOAP 節(jié)點之間交換 SOAP 消息

          SOAP 的主要優(yōu)點是其可擴展性模型,它允許開發(fā)人員使用其他官方消息級標準(WS-Policy、WS-Security、WS-Federation 等)擴展基本協(xié)議,以執(zhí)行特定任務,以及創(chuàng)建自己的擴展。生成的 Web 服務可以用 Web 服務描述語言 (WSDL) 進行記錄,這是一種描述各種端點支持的操作和消息的 XML 文件。

          但是,此方法也是一個缺陷,因為用于發(fā)出請求和接收響應的 XML 可能會變得極其復雜,并且難以閱讀、理解和維護。多年來,許多開發(fā)框架和 IDE 都緩解了此問題,包括 ASP.NET 和 Visual Studio,它們開始提供快捷方式和抽象來簡化開發(fā)體驗并自動生成所需的 XML 代碼。即便如此,與REST相比,該協(xié)議也有幾個缺點:

          • 更少的數(shù)據(jù)格式 - SOAP 僅支持 XML,而 REST 支持更多種類的格式,包括 JavaScript 對象表示法 (JSON),它提供更快的解析速度并且絕對更易于使用,以及逗號分隔值 (CSV),對于帶寬優(yōu)化是主要關注點的大型數(shù)據(jù)集來說,這是一種很好的 JSON 替代方案。
          • 更差的客戶端支持 - 現(xiàn)代瀏覽器和前端框架都經(jīng)過優(yōu)化,可以使用 REST Web 服務,這通常提供更好的兼容性。
          • 性能和緩存問題 — SOAP 消息通常通過 HTTP POST 請求發(fā)送。由于HTTP POST方法是非冪等的,因此它不會在HTTP級別緩存,這使得其請求比RESTful對應項的請求更難緩存。
          • 更慢、更難實現(xiàn) — SOAP Web 服務通常更難開發(fā),尤其是在必須與現(xiàn)有網(wǎng)站或服務集成時。相反,REST 通常可以作為現(xiàn)有代碼的插入功能實現(xiàn),而無需重構整個客戶端-服務器基礎結構。

          由于所有這些原因,今天在REST與SOAP的辯論中,普遍的共識是,除非有特定的原因使用SOAP,否則REST Web API是首選的方式。這些原因可能包括硬件、軟件或基礎結構的限制,以及現(xiàn)有實現(xiàn)的要求,這就是為什么我們不會在本書中使用 SOAP 的原因。

          GraphQL

          2015年,隨著GraphQL的公開發(fā)布,REST的至高無上地位受到質(zhì)疑,GraphQL是Facebook開發(fā)的API的開源數(shù)據(jù)查詢和操作語言。這兩種方法之間的主要區(qū)別不在于體系結構樣式,而在于它們發(fā)送和檢索數(shù)據(jù)的不同方式。

          事實上,GraphQL 遵循大多數(shù) RESTful 約束,依賴于相同的應用層協(xié)議 (HTTP),并采用相同的數(shù)據(jù)格式 (JSON)。但是,它不是使用不同的端點來獲取不同的數(shù)據(jù)對象,而是允許客戶端執(zhí)行動態(tài)查詢并詢問單個端點的具體數(shù)據(jù)要求。

          我將嘗試使用從棋盤游戲 Web API 場景中獲取的實際示例來解釋此概念。假設我們要檢索對 Citadels 棋盤游戲給予積極反饋的所有用戶的名稱和唯一 ID。當我們處理典型的 REST API 時,完成這樣的任務需要以下操作:

          1. 對全文搜索終結點的 Web API 請求 - 檢索名稱等于“Citadels”的棋盤游戲列表。為了簡單起見,讓我們假設這樣的調(diào)用返回單個結果,允許我們獲取目標棋盤游戲的信息,包括它的唯一 ID,這是我們正在尋找的。
          2. 向反饋終結點發(fā)出的另一個 web API 請求 - 檢索從該用戶的唯一 ID 收到的所有反饋。同樣,讓我們假設反饋范圍從 1(“我經(jīng)歷過的最糟糕的游戲體驗”)到 10(“有史以來最好的游戲”)。
          3. 客戶端迭代例如 foreach 循環(huán))- 循環(huán)遍歷所有檢索到的反饋并檢索分級等于或大于 6 的用戶 ID。
          4. 向用戶終端節(jié)點發(fā)出的第三個 Web API 請求 - 檢索與這些唯一 ID 對應的用戶。

          完整的請求/響應周期如圖1.4所示。


          圖 1.4 REST 中的 HTTP 請求-響應周期

          該計劃是可行的,但不可否認的是,它涉及大量工作。具體來說,我們必須執(zhí)行多次往返(三個HTTP請求)和大量的過度獲取 - Citadels游戲信息,所有反饋的數(shù)據(jù)以及所有給予正面評價的用戶的數(shù)據(jù) - 以獲得一些名字。唯一的解決方法是實現(xiàn)額外的 API 端點以返回我們需要的內(nèi)容或簡化一些中間工作。我們可以添加一個端點來獲取給定棋盤游戲 ID 甚至給定棋盤游戲名稱的正面反饋,包括基礎實現(xiàn)中的全文查詢。如果我們愿意,我們甚至可以實現(xiàn)一個專用的端點,例如 api/positiveFeedbacksByName,來執(zhí)行整個任務。

          然而,不可否認的是,這種方法會影響后端開發(fā)時間,并增加我們 API 的復雜性,并且不夠通用,無法在類似的、不相同的情況下提供幫助。如果我們想檢索負面反饋或多個游戲或給定作者創(chuàng)建的所有游戲的正面反饋怎么辦?正如我們很容易理解的那樣,克服這些問題可能并不簡單,特別是當我們在客戶端數(shù)據(jù)獲取要求方面需要高水平的多功能性時。

          現(xiàn)在讓我們看看 GraphQL 會發(fā)生什么。當我們采用這種方法時,我們不是從現(xiàn)有(或附加)端點的角度來考慮,而是專注于我們需要發(fā)送到服務器的查詢以請求我們需要的內(nèi)容,就像我們對 DBMS 所做的那樣。完成后,我們將該查詢發(fā)送到(單個)GraphQL 端點,并在單個 HTTP 調(diào)用中精確地返回我們請求的數(shù)據(jù),而無需獲取任何我們不需要的字段。圖 1.5 顯示了 GraphQL 客戶端到服務器的往返行程。


          圖 1.5 GraphQL 中的 HTTP 請求-響應周期

          正如我們所看到的,性能優(yōu)化不僅限于 Web API。GraphQL 方法不需要客戶端迭代,因為服務器已經(jīng)返回了我們正在尋找的精確結果。

          GraphQL 規(guī)范可在 https://spec.graphql.org 上找到。我將在第 10 章中廣泛討論 GraphQL,向您展示如何將其與 REST 一起使用以實現(xiàn)特定的面向查詢的目標。

          1.2 ASP.NET Core

          現(xiàn)在我們已經(jīng)了解了什么是 Web API,以及如何使用它們在網(wǎng)絡中的 Web 應用程序和服務中交換數(shù)據(jù),現(xiàn)在是時候介紹我們將在本書中創(chuàng)建它們的框架了。該框架 ASP.NET Core,這是一個高性能,跨平臺,開源Web開發(fā)框架,由Microsoft于2016年推出,作為 ASP.NET 的繼任者。

          在以下各節(jié)中,我將簡要介紹其最獨特的方面:整體體系結構、請求/響應管道管理、異步編程模式、路由系統(tǒng)等。

          注意在本書中,我們將使用 .NET 6.0 和 ASP.NET Core Runtime 6.0.11,這是本文撰寫時最新的正式發(fā)布版本,也是繼 .NET Core 1.0、1.1、2.0、2.1、2.2、3.0、3.1 和 .NET 5.0 之后發(fā)布的第九部分。每個版本都引入了一些更新、改進和其他功能,但為了簡單起見,我將回顧最新版本附帶的結果特征。此外,從 .NET 5 開始,所有偶數(shù) .NET 版本(包括 .NET 6)都授予長期支持 (LTS) 狀態(tài),這意味著它們將在未來許多年內(nèi)得到支持,而不是具有較短時間范圍的奇數(shù)版本。目前,偶數(shù)版本的支持期為三年,奇數(shù)版本的支持期為 18 個月 (http://mng.bz/JVpa)。

          1.2.1 體系結構

          我選擇將 ASP.NET Core 用于我們的 Web API 項目,因為新的 Microsoft 框架強制執(zhí)行了幾個現(xiàn)代架構原則和最佳實踐,使我們能夠構建輕量級、高度模塊化的應用程序,這些應用程序具有高水平的可測試性和源代碼可維護性。這種方法自然會指導開發(fā)人員構建(或采用)由離散和可重用組件組成的應用程序,這些組件執(zhí)行特定任務并通過框架提供的一系列接口進行通信。這些組件稱為服務和中間件,它們在 Web 應用程序的 Program.cs 文件中注冊和配置,該文件是應用的入口點。

          注意如果您來自較舊的 ASP.NET Core 版本(如 3.1 或 5.0),您可能想知道 Startup 類(及其相應的 Startup.cs 文件)發(fā)生了什么變化,該文件用于包含服務和中間件配置設置。此類已從 .NET 6 引入的最小宿主模型中刪除,該模型將程序類和啟動類合并到單個 Program.cs 文件中。

          服務業(yè)

          服務是應用程序提供其功能所需的組件。我們可以將它們視為應用依賴項,因為我們的應用依賴于它們的可用性才能按預期工作。我在這里使用術語依賴關系是有原因的:ASP.NET Core 支持依賴注入 (DI) 軟件設計模式,這是一種架構技術,允許我們在類與其依賴關系之間實現(xiàn)控制反轉(zhuǎn)。以下是整個服務實現(xiàn)、注冊/配置和注入過程在 ASP.NET Core 中的工作方式:

          • 實現(xiàn) - 每個服務都通過專用接口(或基類)來實現(xiàn),以抽象實現(xiàn)。接口和實現(xiàn)都可以由框架提供、由開發(fā)人員創(chuàng)建或從第三方(GitHub、NuGet 包等)獲取。
          • 注冊和配置 - 應用程序使用的所有服務都在內(nèi)置的 IServiceProvider 類(服務容器)中進行配置和注冊(使用其接口)。實際的注冊和配置過程發(fā)生在 Program.cs 文件中,開發(fā)人員還可以在其中選擇合適的生存期(瞬態(tài)、作用域或單一實例)。
          • 依賴關系注入 - 可以將每個服務注入到要使用它的類的構造函數(shù)中。框架通過 IServiceProvider 容器類自動提供依賴項的實例,創(chuàng)建新的實例或可能重用現(xiàn)有實例,具體取決于配置的服務生存期,并在不再需要時釋放它。

          框架提供的服務接口的典型示例包括可用于實現(xiàn)基于策略的權限的 IAuthorizationService,以及可用于發(fā)送電子郵件的 IEmailService。

          中間件

          中間件是一組在 HTTP 級別運行的組件,可用于處理整個 HTTP 請求處理管道。如果您還記得 ASP.NET 在 ASP.NET Core 出現(xiàn)之前使用的 HttpModules 和 HttpHandler,您可以很容易地看到中間件如何扮演類似的角色并執(zhí)行相同的任務。ASP.NET 核心 Web 應用程序中使用的中間件的典型示例包括 HttpsRedirectionMiddleware,它將非 HTTPS 請求重定向到 HTTPS URL,以及 AuthorizationMiddleware,它在內(nèi)部使用授權服務來處理 HTTP 級別的所有授權任務。

          每種類型的中間件都可以將 HTTP 請求傳遞給管道中的下一個組件或提供 HTTP 響應,從而縮短管道本身并阻止其他中間件處理請求。這種類型的請求阻止中間件稱為終端中間件,通常負責處理各種應用端點的主要業(yè)務邏輯任務。

          警告值得注意的是,中間件是按注冊順序(先進先出)處理的。始終在程序.cs文件中的非終端中間件之后添加終端中間件;否則,該文件將不起作用。

          終端中間件的一個很好的例子是 StaticFileMiddleware,它最終處理指向靜態(tài)文件的端點 URL,只要它們是可訪問的。發(fā)生這種情況時,它會使用適當?shù)?HTTP 響應將請求的文件發(fā)送給調(diào)用方,從而終止請求管道。我之前簡要提到的 HttpsRedirectionMiddleware 也是終端中間件,因為它最終會響應所有非 HTTPS 請求的 HTTP-to-HTTPS 重定向。

          注意StaticFileMiddleware 和 HttpsRedirectionMiddleware 只有在滿足某些情況時才會終止 HTTP 請求。如果請求的終結點與其激活規(guī)則不匹配(例如,對于前者,指向不存在的靜態(tài)文件的 URL,或者對于后者,指向已在 HTTPS 中),則會將其傳遞給管道中存在的下一個中間件,而無需執(zhí)行任何操作。出于這個原因,我們可以說它們可能是終端中間件,以區(qū)別于總是在 HTTP 請求到達請求管道時結束請求管道的中間件。

          1.2.2 Program.cs

          現(xiàn)在我已經(jīng)介紹了服務和中間件,我們終于可以看一下在應用程序開始時執(zhí)行的 Program.cs 文件:

          var builder=WebApplication.CreateBuilder(args);    ?
           
          // Add services to the container.
           
          builder.Services.AddControllers();                   ?
          // Learn more about configuring Swagger/OpenAPI
          // at https://aka.ms/aspnetcore/swashbuckle
          builder.Services.AddEndpointsApiExplorer();          ?
          builder.Services.AddSwaggerGen();                    ?
           
          var app=builder.Build();                           ?
           
          // Configure the HTTP request pipeline.
          if (app.Environment.IsDevelopment())
          {
              app.UseSwagger();                                ?
              app.UseSwaggerUI();                              ?
          }
           
          app.UseHttpsRedirection();                           ?
           
          app.UseAuthorization();                              ?
           
          app.MapControllers();                                ?
           
          app.Run();                                           ?

          ? 創(chuàng)建 WebApplicationBuilder 工廠類

          ? 注冊和配置服務

          ? 構建 Web 應用程序?qū)ο?/p>

          ? 注冊和配置非終端和潛在的終端中間件

          ? 注冊和配置終端中間件

          通過分析這段代碼,我們可以很容易地看到該文件負責以下初始化任務:

          • 實例化 Web 應用程序
          • 注冊和配置服務
          • 注冊和配置中間件

          更準確地說,Web 應用程序由 WebApplicationBuilder 工廠類實例化,從而創(chuàng)建一個 WebApplication 對象。此實例存儲在應用局部變量中,用于注冊和配置所需的服務和中間件。

          注意服務和中間件都通過專用的擴展方法注冊和配置,這是一種簡化設置過程并保持程序.cs文件盡可能簡潔的便捷方法。在 ASP.NET 核心命名約定下,服務主要通過使用帶有 Add 前綴的擴展方法進行配置;中間件前綴為“使用”、“映射”和“運行”。至少目前,我們關心的唯一區(qū)別是 Run 委托始終是 100% 終端和最后一個要處理的。稍后,我們將在試驗最小 API 時詳細討論這些約定及其含義。

          為了簡單起見,我將中間件分為兩類:潛在終端和終端。在這一點上,區(qū)別應該很明顯。潛在的終端中間件只有在與某些規(guī)則匹配時才結束HTTP請求管道,而終端中間件總是這樣做(所以難怪它是最后一個)。

          1.2.3 控制器

          在 ASP.NET Core 中,控制器是一個類,用于對一組處理類似 HTTP 請求的操作方法(也稱為操作)進行分組。從這個角度來看,我們可以說控制器是可用于聚合具有共同點的操作方法的容器:路由規(guī)則和前綴、服務、實例、授權要求、緩存策略、HTTP 級過濾器等。這些常見要求可以直接在控制器類上定義,從而避免了為每個操作指定這些要求的需要。此方法由一些強大的內(nèi)置命名和編碼約定強制執(zhí)行,有助于保持代碼干燥并簡化應用的體系結構。

          注意 DRYDon't Repeat Yourself 的首字母縮寫詞,這是一個眾所周知的軟件開發(fā)原則,可幫助我們記住避免冗余、模式重復以及可能導致代碼異味的任何其他內(nèi)容。它與 WET 相反(將所有內(nèi)容寫入兩次每次寫入)。

          從 ASP.NET Core 開始,控制器可以從兩個內(nèi)置基類繼承:

          • ControllerBase,不支持視圖的最小實現(xiàn)
          • 控制器,一個更強大的實現(xiàn),它繼承自 ControllerBase 并添加了對視圖的完全支持

          正如我們很容易理解的那樣,控制器基類旨在用于采用模型-視圖-控制器 (MVC) 模式的 Web 應用程序,其中它們旨在返回來自業(yè)務邏輯(通常由依賴注入的服務處理)的數(shù)據(jù)。在典型的 ASP.NET Core Web 應用程序中,生成的數(shù)據(jù)通過視圖返回到客戶端,這些視圖使用客戶端標記和腳本語言(如 HTML、CSS、JavaScript 等)處理應用程序的數(shù)據(jù)表示層(或服務器端呈現(xiàn)的語法,如 Razor)。在處理 Web API 時,我們通常不使用視圖,因為我們希望直接從控制器返回 JSON 或 XML 數(shù)據(jù)(無 HTML)。在這種情況下,從 ControllerBase 基類繼承是在特定情況下的推薦方法。下面是處理兩種類型的 HTTP 請求的示例 Web API 控制器:

          • 對 /api/Sample/ 的 GET 請求以接收項目列表
          • 對 /api/Sample/{id} 的 GET 請求,用于接收具有指定 {id} 的單個項目,假設它是一個充當主鍵的唯一整數(shù)值
          • 對 /api/Sample/{id} 的 DELETE 請求,以刪除具有指定 {id} 的單個項
          [ApiController]                                ?
          [Route("api/[controller]")]                    ?
          public class SampleController : ControllerBase
          {
              public SampleController()
              {
              }
           
              [HttpGet]                                  ?
              public string Get()
              {
                  return "TODO: return all items";
              }
           
              [HttpGet("{id}")]                          ?
              public string Get(int id)
              {
                  return $"TODO: return the item with id #{id}";
              }
           
              [HttpDelete("{id}")]                       ?
              public string Delete(int id)
              {
                  return $"TODO: delete the item with id #{id}";
              }
          }

          ? 添加特定于 API 的行為

          ? 默認路由規(guī)則

          ? 處理 HTTP GET 到 /api/Sample/ 的操作

          ? 處理 HTTP GET 到 /api/Sample/{id} 的操作

          ? 處理 HTTP DELETE 到 /api/Sample/{id} 的操作

          正如我們通過查看此代碼所看到的,通過利用一些有用的內(nèi)置約定,我們能夠用幾行代碼完成給定的任務:

          • 一個集中式的 /api/Sample/ 路由前綴,對所有操作方法都有效,這要歸功于在控制器級別應用的 [Route(“api/[controller]”)] 屬性
          • 所有已實現(xiàn)的 HTTP 謂詞(包括必需參數(shù))的自動路由規(guī)則,這要歸功于應用于相應操作方法的 [HttpGet] 和 [HttpDelete] 屬性
          • 使用控制器中間件的默認規(guī)則進行自動路由映射,因為我們將控制器后綴應用于類名,只要應用。MapControllers() 擴展方法存在于程序.cs文件中

          注意此外,由于我們使用了 [ApiController] 屬性,因此我們還需要一些特定于 Web API 的其他約定,這些約定會自動返回某些 HTTP 狀態(tài)代碼,具體取決于操作類型和結果。我們將在第6章中詳細討論它們,屆時我們將深入研究錯誤處理。

          1.2.4 最小接口

          具有內(nèi)置約定的控制器是使用幾行代碼實現(xiàn) Web API 業(yè)務邏輯的好方法。然而,在 ASP.NET Core 6中,該框架引入了一個最小的范式,允許我們以更少的儀式來構建Web API。這個特性被稱為最小API,盡管它很年輕,但它得到了新的和經(jīng)驗豐富的 ASP.NET 開發(fā)人員的大量關注,因為它通常允許更小,更易讀的代碼庫。

          解釋最小 API 的最好方法是在基于控制器的方法和塊上的新孩子之間執(zhí)行快速代碼比較。以下是我們使用 SampleController 處理的相同 HTTP 請求如何由最小 API 處理,并對程序.cs文件進行一些小的更新:

          // app.MapControllers();                                  ?
          app.MapGet("/api/Sample",                                 ?
              ()=> "TODO: return all items");                      ?
          app.MapGet("/api/Sample/{id}",                            ?
              (int id)=> $"TODO: return the item with id #{id}");  ?
          app.MapDelete("/api/Sample/{id}",                         ?
              (int id)=> $"TODO: delete the item with id #{id}");  ?

          ? 刪除此行(和 SampleController 類)

          ? 改為添加這些行

          如我們所見,所有實現(xiàn)都傳輸?shù)?Program.cs 文件中,而無需單獨的控制器類。此外,代碼可以進一步簡化(或DRYfied),例如通過添加前綴變量,無需多次重復“/api/Sample”。在代碼可讀性、簡單性和減少開銷方面,其優(yōu)勢顯而易見。

          提示任何最小 API 方法的業(yè)務邏輯都可以放在程序.cs文件之外;只需要 Map 方法才能到達那里。事實上,將實際實現(xiàn)移動到其他類幾乎總是很好的做法,除非我們處理的是單行代碼。

          最小 API 范例不太可能取代控制器,但它肯定會簡化大多數(shù)小型 Web API 項目(如微服務)的編碼體驗,并吸引大多數(shù)尋求時尚方法和淺學習曲線的開發(fā)人員。出于所有這些原因,我們將在本書中經(jīng)常將其與控制器一起使用。

          1.2.5 基于任務的異步模式

          影響 Web 應用程序的大多數(shù)性能問題都是由于有限數(shù)量的線程需要處理可能無限量的并發(fā) HTTP 請求。在響應這些請求之前,這些線程必須執(zhí)行一些資源密集型(通常是不可緩存的)任務,在最終收到結果之前被阻止時,尤其如此。典型的示例包括通過 SQL 查詢從 DBMS 讀取或?qū)懭霐?shù)據(jù)、訪問第三方服務(如外部網(wǎng)站或 Web API)以及執(zhí)行任何其他需要大量執(zhí)行時間的任務。

          當 Web 應用程序被迫同時處理大量這些請求時,可用線程的數(shù)量會迅速減少,從而導致響應時間變慢,并最終導致服務不可用(HTTP 503 等)。

          注意這種情況是大多數(shù)基于 HTTP 的拒絕服務 (DoS) 攻擊的主要目標,在這種攻擊中,Web 應用程序被請求淹沒,使整個服務器不可用。線程阻塞、非緩存的 HTTP 請求是 DoS 攻擊者的金礦,因為他們很有可能迅速耗盡線程池。

          處理資源密集型任務的一般經(jīng)驗法則是積極緩存生成的 HTTP 響應,如 REST 約束 3(可緩存性)所述。然而,在某些情況下,緩存不是一種選擇,例如當我們需要寫入DBMS(HTTP POST)或讀取可變或高度可定制的數(shù)據(jù)(帶有大量參數(shù)的HTTP GET)時。在這種情況下,我們該怎么辦?

          ASP.NET Core 框架允許開發(fā)人員通過實現(xiàn) C# 語言級異步模型(通常稱為基于任務的異步模式TAP))來有效地處理此問題。了解TAP如何工作的最好方法是看到它的實際效果。請看一下以下源代碼:

          [HttpGet]
          public async Task<string> Get()           ?
          {
              return await Task.Run(()=> {         ?
                  return "TODO: return all items";
              });
          }

          ? 異步

          ? 等待

          如我們所見,我們對之前用于實現(xiàn) TAP 的 SampleController 的第一個操作方法應用了一些小的更新。新模式依賴于使用 async 和 await 關鍵字以非阻塞方式處理任務:

          • async 關鍵字定義返回任務的方法。
          • await 關鍵字允許調(diào)用線程以非阻塞方式啟動新實現(xiàn)的任務。任務完成后,線程繼續(xù)執(zhí)行代碼,從而返回生成的逗號分隔字符串。

          值得注意的是,因為我們在 Get() 操作方法中使用了 await 關鍵字,所以我們也必須將該方法標記為異步。這完全沒問題;這意味著該方法將由調(diào)用線程等待而不是阻塞它,這是我們想要的。

          我們的最小實現(xiàn)在性能方面沒有任何好處。我們絕對不需要等待像字面 TODO 字符串這樣微不足道的事情。但是它應該讓我們了解當示例 Task.Run 被需要服務器執(zhí)行一些實際工作(例如從 DBMS 檢索實際數(shù)據(jù))的內(nèi)容所取代時,異步/等待模式將為 Web 應用程序帶來的好處。我將在第 4 章中廣泛討論異步數(shù)據(jù)檢索任務,其中介紹了 Microsoft 最流行的 .NET 數(shù)據(jù)訪問技術:實體框架核心。

          總結

          • Web API 是基于 HTTP 的服務,任何軟件應用程序都可以使用它來訪問和可能操作數(shù)據(jù)。
          • Web API 旨在通過使用通用通信標準(協(xié)議)、給定的可用操作集(規(guī)范)和數(shù)據(jù)交換格式(JSON、XML 等)來工作。
          • Web API 通常分為四個可能的使用范圍:
            • 公開開放 - 當任何人擁有(或可以獲得)訪問權限時
            • 合作伙伴 - 當訪問權限僅限于業(yè)務伙伴時
            • 內(nèi)部專用 - 當訪問權限僅限于組織的網(wǎng)絡時
            • 復合 - 當他們編排多個公共、合作伙伴和/或內(nèi)部 API 調(diào)用時
          • 為了履行其職責,Web API 需要一組統(tǒng)一的規(guī)則、約束和格式,具體取決于所選的體系結構樣式或消息傳遞協(xié)議。目前最常用的標準是 REST、SOAP、gRPC 和 GraphQL:
            • 每種方法都有獨特的特征、權衡和支持的格式。
            • 本書主要關注 REST,但第 10 章專門介紹 GraphQL。
          • ASP.NET Core 是一個高性能、跨平臺、開源的 Web 開發(fā)框架,我們將在本書中使用它來設計和開發(fā) Web API:
            • ASP.NET Core 的現(xiàn)代架構原則允許開發(fā)人員構建具有高度可測試性和可維護性的輕量級、高度模塊化的應用程序。
          • ASP.NET Core 體系結構允許開發(fā)人員通過使用離散和可重用的組件來執(zhí)行特定任務來自定義應用的功能和工作負載。這些組件分為兩個主要類別,這兩個類別都在應用的入口點(程序.cs文件中注冊和配置:
            • 服務 - 需要提供功能并通過依賴關系注入進行實例化
            • 中間件 — 負責處理 HTTP 請求管道
          • ASP.NET Core 支持兩種構建 Web API 的范式:控制器,它提供了極大的多功能性和一整套受支持的功能,以及最小的 API,這是一種簡化的方法,允許我們編寫更少的代碼,同時減少開銷:
            • .NET 6 中引入的最小 API 方法可能是以省時的方式構建簡單 Web API 的好方法,非常適合正在尋找時尚、現(xiàn)代編程方法的開發(fā)人員。
          • ASP.NET Core Web 應用可以通過使用 TAP 來克服由于資源密集型線程阻塞調(diào)用而導致的大多數(shù)性能問題,TAP 是一種異步編程模型,允許主線程以非阻塞方式啟動任務并在任務完成后恢復執(zhí)行。

          主站蜘蛛池模板: 中文字幕在线观看一区| 成人无码一区二区三区| 亚洲视频免费一区| 在线播放偷拍一区精品| 国产中的精品一区的| 日韩精品无码一区二区中文字幕 | 无码国产亚洲日韩国精品视频一区二区三区| 一区二区三区高清| 成人在线一区二区| 伊人色综合视频一区二区三区| 国偷自产Av一区二区三区吞精| 国产SUV精品一区二区88L| 精品国产一区二区三区久久| 大香伊蕉日本一区二区| 久久久久99人妻一区二区三区| 一区二区三区在线观看中文字幕| 国产精品日韩欧美一区二区三区 | 亚洲蜜芽在线精品一区| 亚洲成人一区二区| 日韩伦理一区二区| 在线欧美精品一区二区三区| 国产免费一区二区三区在线观看| 一区二区乱子伦在线播放| 在线视频国产一区| 精品免费国产一区二区三区 | 精品一区二区三区自拍图片区| 国产伦精品一区二区三区| 亚洲欧美成人一区二区三区 | 亚洲天堂一区二区三区| 亚洲乱码一区二区三区在线观看| 久久国产精品一区| 无码精品人妻一区二区三区免费| 日本高清无卡码一区二区久久| 国产成人av一区二区三区不卡 | 国产麻豆剧果冻传媒一区| 无码夜色一区二区三区| 在线播放国产一区二区三区 | 国产精品成人国产乱一区| 亚洲一本一道一区二区三区| 91精品国产一区| 亚洲国产系列一区二区三区|