日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

dotNET Core 3.X 使用 Web API

發布時間:2023/12/4 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dotNET Core 3.X 使用 Web API 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

現在的 Web 開發大多都是前后端分離的方式,后端接口的正確使用顯得尤為重要,本文講下在 dotNET Core 3.X 下使用 Web API 。

環境

  • 操作系統:Mac

  • IDE:Rider

  • dotNET Core:3.1

創建項目

如果是 Windows 操作系統當然是首選 VS2019 ,在 Mac 中雖然也有 VS2019 For Mac,但還是感覺 Rider 比較好用(調試和智能提示),在 Rider 中創建 Web API 項目:

3.x 和 2.x 區別

1、Program 類的 IWebHostBuilder 修改為了 IHostBuilder,這一塊的改動如果是直接使用 3.x 可以不用過于關心,如果是從 2.x 升級到 3.x,就要注意了,兩個 Program 類對比結果如下圖:

2、Startup 類的區別如下圖:

最重要的是在 3.x 中使用的是 services.AddControllers(); 來注冊服務,相比?2.x 中的 services.AddMvc() 更加輕量級,因為在 AddMvc 方法中添加了很多 Web API 不需要的功能,如下圖:

3、3.x 引入了新的 JSON API ,新的 JSON API 使用更少的內存,擁有更快的執行速度,引用 using System.Text.Json; 就可以使用,如果需要使用原來的功能,需要引入 Nuget包:Microsoft.AspNetCore.Mvc.NewtonsoftJson

另:

  • 有關 3.x 中被刪除的程序集可以參考這里:https://github.com/dotnet/aspnetcore/issues/3755

  • 有關 3.x 中性能提升可以參考這篇文章:https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

[ApiController] 特性

在 3.x 中默認項目模板中會創建的一個名為 WeatherForecastController 的控制器,按照約束控制器類以 Controller 結尾。

可以看到在 WeatherForecastController 類的上面自動添加了 [ApiController] 特性,添加此特性后,會對 Api 功能有所加持,比如:

自動模型狀態驗證

意思是當客戶端傳遞的模型數據(輸入參數)不符合要求時,在接口方法中不需要做任何處理,接口會自動返回 400 的錯誤,看下面的例子:

1、創建 UserController 類,并將 [ApiController] 特性注釋掉;
2、添加 User 類,將 Name 屬性設置為 Required;

public?class?User {[Required]public??string?Name?{?get;?set;?}public?string?Code?{?get;?set;?} }

3、在?UserController 類中添加 AddUser 方法

[HttpPost] [Route("adduser")] public?ActionResult?AddUser(User?user) {????return?Ok(); }

4、使用 Postman 調用,沒有添加任何參數,返回的結果為?200

這個結果不是我們所期望的,之前沒有 [ApiController] 特性的時候,需要在接口方法中處理,如下:

[HttpPost] [Route("adduser")] public?ActionResult?AddUser(User?user) {if?(!ModelState.IsValid){return?BadRequest((ModelState));}return?Ok(); }

5、再用 Postman 調用,結果如下:

6、現在添加上 [ApiController] 特性,并將 AddUser 中的校驗邏輯去掉,再次使用 Postman,結果如下:

推斷參數綁定源

之前需要在參數上添加 [FromBody]、[FromQuery]等特性,現在可以去掉這些特性,系統會自動推斷參數的來源,比如:如果一個參數在 Route 里面定義了,會自動從先從Path 查找,沒找到會從查詢參數上查找然后進行綁定。

錯誤狀態碼詳細信息

之前的版本中,如果接口返回一個 BadRequest,是沒有內容的,只有狀態碼,如下:

加上 [ApiController] 特性后,結果如下:

基類

在 3.x 中創建控制器后,默認的基類為 ControllerBase ,該類中提供了 OK、BadRequest 等常用方法給我們使用。

在我們實際開發中,通常會自定義添加一個所有 ?Controller 類的基礎類,一些通用的功能可以放到基類中,比如,對 AutoMapper 的注入,代碼如下:

public?class?BaseController:?ControllerBase {private?readonly?IMapper?_mapper;public?BaseController(IMapper?mapper){_mapper?=?mapper;}public?IMapper?Mapper?=>?_mapper; }

HTTP 方法

先看下面這張圖

按照標準的 RESTful Web API 風格,不同的請求動作需要使用相對應的方法,但實際我們最常用的是 GET 和 POST,查詢使用 GET,其他的操作都是使用 POST。

HTTP 狀態碼

正確的返回狀態碼有助于客戶端分析請求返回結果和問題排查,常用的狀態碼如下:

常見的一個問題:由于客戶端參數的問題,導致接口代碼中執行異常了,最終返回了 500,導致排查問題非常復雜,還需要還原問題場景下的數據和入參。正確的做法應該是對參數做相關校驗最終返回相應的 4XX 的狀態碼。

輸入參數

模型綁定

接口的輸入參數就是通過模型綁定將 HTTP 請求中的值映射到參數中,模型綁定有以下六種:

  • [FromRoute]:通過路由的 URL 中取值,可以自動推斷;

  • [FromQuery]:獲取 URL 地址中的參數,可以自動推斷;

  • [FromBody]:從HTTP Body取值,通常用于取JSON, XML,可以自動推斷;

  • [FromHeader]:獲取 Request Header 中的參數信息,需要指定

  • [FromForm]:獲取 Content-Type 為 multipart/form-data 或 application/x-www-form-urlencoded 類型的參數,需要指定

  • [FromServices]:獲取依賴注入的參數,依賴注入默認是使用構造函數注入,但Controller 可能會因為每個Action用到不一樣的 Service 導致很多參數,所以也可以在 Action 注入Service,需要指定。

下面實現一個使用 [FromServices] 的示例:

1、創建 IUserService 接口和 UserService 類,代碼如下:

public?interface?IUserService {string?GetUserName(string?userId); } public?class?UserService:IUserService {public?string?GetUserName(string?userId){return?$"UserName:{userId}";} }

2、在 Startup 類的 ConfigureServices 方法中添加下面代碼進行注冊

services.AddScoped<IUserService,UserService>();

3、添加 UserController 類,里面添加名為 GetUserName 的 Action 方法

[HttpGet]public?ActionResult<string>?GetUserName(string?userId,??[FromServices]IUserService?userService) {return?Ok($"{userService.GetUserName(userId)}"); }

4、執行結果如下:

參數驗證

參數驗證是非常重要的,否則本來是 4XX 的問題就會變成 5XX 的問題,參數驗證有這么幾種:

  • Data Annotations

  • 自定義 Attribute

  • 實現 IValitableObject 接口

  • 使用第三方的驗證庫,比如 FluentValidation

Data Annotations

1、在 User 的實體類上添加相關特性

public?class?User {[Required(ErrorMessage?=?"姓名不能為空")]public?string??Name?{?get;?set;?}[EmailAddress(ErrorMessage?=?"郵件格式不正確")]public?string??Email?{?get;?set;?} }

2、調用結果如下:

有關更多的 Data Annotations 特性的使用,可以參考官方文檔:https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=netcore-3.1

IValitableObject 接口

1、將 User 類繼承 IValitableObject 接口,并實現 Validate 方法,代碼如下:

public?class?User:?IValidatableObject {[Required(ErrorMessage?=?"姓名不能為空")]public?string??Name?{?get;?set;?}[EmailAddress(ErrorMessage?=?"郵件格式不正確")]public?string??Email?{?get;?set;?}public?IEnumerable<ValidationResult>?Validate(ValidationContext?validationContext){if?(Name?==?Email){yield?return?new?ValidationResult("名稱不能和郵箱相等",new?[]{nameof(Name),nameof(Email)});}} }

2、調用結果如下:

自定義 Attribute

自定義 Attribute 功能和 IValitableObject 接口類似,但可以作用于類級別也能用于屬性級別,更加靈活。

1、創建 NameNotEqualEmailAttribute 類,用來實現判斷 User 類中的名稱和郵箱不能相等

public?class?NameNotEqualEmailAttribute?:?ValidationAttribute {protected?override?ValidationResult?IsValid(object?value,?ValidationContext?validationContext){var?user?=?validationContext.ObjectInstance?as?User;if?(user.Name?==?user.Email){return?new?ValidationResult("名稱不能和郵箱相等",new?[]{nameof(User)});}return?ValidationResult.Success;} }

2、在 User 類上添加此特性

[NameNotEqualEmail] public?class?User {[Required(ErrorMessage?=?"姓名不能為空")]public?string??Name?{?get;?set;?}[EmailAddress(ErrorMessage?=?"郵件格式不正確")]public?string??Email?{?get;?set;?} }

3、調用結果如下:

FluentValidation

FluentValidation 就不多做介紹了,可以參見官方文檔:https://fluentvalidation.net/

ModelBinder

ModelBinder 是自定義模型綁定器,可以對入參的類型進行一些轉換,比如,參數中傳遞 001,002 這樣的字符串,在接口中使用 IEnumerable來進行接收。

1、創建 StringToListModelBinder 類,如下:

public?class?StringToListModelBinder:?IModelBinder { public?Task?BindModelAsync(ModelBindingContext?bindingContext) {if?(!bindingContext.ModelMetadata.IsEnumerableType){bindingContext.Result?=?ModelBindingResult.Failed();return?Task.CompletedTask;}var?value?=?bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ToString();if?(string.IsNullOrWhiteSpace(value)){bindingContext.Result?=?ModelBindingResult.Success(null);return?Task.CompletedTask;}var?elementType?=?bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];var?converter?=?TypeDescriptor.GetConverter(elementType);var?values?=?value.Split(new[]?{','},?StringSplitOptions.RemoveEmptyEntries).Select(x?=>?converter.ConvertFromString(x.Trim())).ToArray();var?typedValues?=?Array.CreateInstance(elementType,?values.Length);values.CopyTo(typedValues,0);bindingContext.Model?=?typedValues;bindingContext.Result?=?ModelBindingResult.Success(bindingContext.Model);return?Task.CompletedTask; }

2、在 UserController 類中創建 GetUsersByIds 方法

[HttpGet("ids")] public?ActionResult<List<User>>?GetUsersByIds([ModelBinder(BinderType?=?typeof(StringToListModelBinder))]IEnumerable<string>?ids) {if?(ids?==?null){return?BadRequest();}return?Ok();}

3、調用結果

返回值

返回 XML 格式

盡管使用 Web API 通常都是使用 JSON 格式,但有些時候需要返回 XML 格式,默認情況下,即使請求頭中添加了 Accept=application/xml,接口依然會返回 JSON 格式的結果,想要返回 XML 格式,修改 Startup 類的 ConfigureServices 方法即可。

services.AddControllers().AddXmlDataContractSerializerFormatters();

結果如下:

錯誤信息統一返回

之前的文章中有講過使用過濾器的方式來做到結果的統一返回。這里介紹另一種方式,使用 ConfigureApiBehaviorOptions ,可以讓我們自定義錯誤信息的返回內容和格式。修改 Startup 類中的 ConfigureServices 方法

services.AddControllers().AddXmlDataContractSerializerFormatters().ConfigureApiBehaviorOptions(setup?=>{setup.InvalidModelStateResponseFactory?=?context?=>{var?details?=?new?ValidationProblemDetails(context.ModelState){Type?=?"http://api.oec2003.com/help",Title?=?"實體驗證錯誤",Status?=?StatusCodes.Status422UnprocessableEntity,Detail?=?"看詳細",Instance?=?context.HttpContext.Request.Path,};details.Extensions.Add("trachid",context.HttpContext.TraceIdentifier);return?new?UnprocessableEntityObjectResult(details){ContentTypes?=?{?"application/problem+json"?}};};});

當出現驗證問題時,結果如下:

更多詳細信息可以看文檔:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/handle-errors?view=aspnetcore-3.1

數據塑形

在 API 中返回結果到前端時,一般不會直接將底層的 Entity 返回,會創建相對應的 Dto,比如,用戶的 Entity 是這樣的

public?class?User {public?string??Name?{?get;?set;?}public?string??Email?{?get;?set;?}public?string??Password?{?get;?set;?} }

創建 User 的 Dto 類 UserDto,如下

public?class?UserDto {public?string??Name?{?get;?set;?}public?string??Email?{?get;?set;?}}

在接口的 Action 方法中使用 AutoMapper 做下轉換

[HttpGet("{userId}")] public?ActionResult<UserDto>?GetUserById(string?userId) {User?user?=?new?User(){Name?=?"oec2003",Email?=?"oec2003@qq.com",Password?=?"123456"};return?Ok(base.Mapper.Map<UserDto>(user)); }

請求結果如下:

同樣的接口在前端不同的場景下需要返回不一樣的字段數據,一種方式是創建很多不同的接口,返回不同的 Dto 的結果,但這樣做非常繁瑣,可以通過 ExpandoObject 來實現按客戶端的需要進行返回結果,具體步驟如下:

1、因為獲取用戶列表的接口方法的是 List,所以先創建一個 IEnumerable 的擴展方法,該擴展方法用于根據傳進的字段參數來組裝返回的結果,代碼如下:

public?static?class?IEnumerableExtension {public?static?IEnumerable<ExpandoObject>?GetData<T>(this?IEnumerable<T>?source,?string?fields){if?(source?==?null){throw?new?ArgumentNullException(nameof(source));}var?objectList?=?new?List<ExpandoObject>(source.Count());var?propertyInfoList?=?new?List<PropertyInfo>();if?(string.IsNullOrWhiteSpace(fields)){var?propertyInfos?=?typeof(T).GetProperties(BindingFlags.Public?|BindingFlags.Instance);propertyInfoList.AddRange(propertyInfos);}else{var?fieldSplit?=?fields.Split(',');foreach?(var?field?in?fieldSplit){var?propertyName?=?field.Trim();var?propertyInfo?=?typeof(T).GetProperty(propertyName,BindingFlags.IgnoreCase?|?BindingFlags.Public?|?BindingFlags.Instance);if?(propertyInfo?==?null){throw??new?Exception($"屬性名:{propertyName}?沒有找到");}propertyInfoList.Add(propertyInfo);}}foreach?(T?t?in?source){var?obj=new?ExpandoObject();foreach?(var?propertyInfo?in?propertyInfoList){var?value?=?propertyInfo.GetValue(t);((IDictionary<string,?object>)?obj).Add(propertyInfo.Name,?value);}objectList.Add(obj);}return?objectList;} }

2、創建獲取用戶列表的 Action 方法

[HttpGet] public?ActionResult?GetUsers([FromBody]string?fields) {var?userList?=new?List<User>()?{new?User(){?Name?=?"oec2003",Email?=?"oec2003@qq.com",Password?=?"123456"},new?User(){?Name?=?"oec2004",Email?=?"oec2004@qq.com",Password?=?"123456"},new?User(){?Name?=?"oec2004",Email?=?"oec2004@qq.com",Password?=?"123456"}};var?returnResult?=?base.Mapper.Map<List<UserDto>>(userList);//使用擴展方法按需獲取return?Ok(returnResult.GetData(fields)); }

3、查看調用結果

返回一個屬性 Name

返回所有

最后

本文只是涉及了在 Web API 中比較常用的一些功能點,限于篇幅,每個點并沒有寫的非常深入,也較少涉及原理,但我們在學習過程中,除了實現效果外還應該深入去了解其中細節和原理。

文中示例代碼:https://github.com/oec2003/DotNetCoreThreeAPIDemo

希望本文對您有所幫助。

總結

以上是生活随笔為你收集整理的dotNET Core 3.X 使用 Web API的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。