目錄
介紹
預設的ASP.NET Core API響應
AutoWrapper.Core的救援
主要特點:
TL,DR 給我看代碼
定義自己的自定義消息
定義自己的Api異常
選項
版本1.0.0
1.1.0版添加
支援日志
支援Swagger
總結
GitHub Repo
參考文獻
介紹
在為“實際”應用程式項目建構API時,大多數開發人員忘記了向其消費者提供有意義的響應的重要性。發生這種情況有幾個原因;可能是他們的開發時間有限,他們沒有标準的HTTP 響應格式,或者隻是隻要API 将所需的資料傳回給消費者,他們就不在乎響應。嗯,API不僅要通過 HTTP傳遞JSON ,還應如何向使用它的開發人員提出有意義的響應。
就像有人曾經說過的…
“一個好的API設計是使用它的開發人員的UX。”
作為重視消費者的API 開發者,我們希望對他們做出有意義且一緻的API響應。
ASP.NET Core 使我們能夠快速建立REST API。但是,對于開箱即用的成功請求和錯誤,它們不會提供一緻的響應。如果你對API采用RESTful方式,那麼你将被使用HTTP動詞,如GET、POST、PUT和DELETE。根據您的方法/操作的設計方式,每個操作都可能傳回不同的類型。你的POST、PUT和DELETE終端可能會傳回一個資料,或者根本沒有。您的GET終端可能傳回一個 string、 List<T>、某種類型的IEnumerable ,甚至是一個object。另一方面,如果您的API 抛出錯誤,它将傳回一個object 或更糟糕的指出錯誤原因的 HTML 字元串。所有這些響應之間的差異使得使用API變得很困難,因為使用者需要了解每種情況下傳回的資料的類型和結構。客戶代碼和服務代碼都變得難以管理。
去年,我建立了兩個用于管理異常和響應一緻性的Nuget 包,使用一個自定義的用于restful API 的object包裝器。
令我驚訝的是,這兩個軟體包現在都有數百次下載下傳,并且被Blazor Boilerplate等其他開源項目使用。
雖然我認為這兩個軟體包都成功,但它們仍然存在一些故障,是以我決定建立一個新軟體包來重構代碼庫,應用錯誤修複并為其添加新功能。
在本文中,我們将研究如何使用AutoWrapper來美化我們的ASP.NET Core API響應。
預設的ASP.NET Core API響應
當您建立新的ASP.NET Core API模闆時,Visual Studio将建構所有必需的檔案和依賴項,以幫助您開始建構RESTful API。生成的模闆包含一個“WeatherForecastController”,用于使用靜态資料模拟簡單的GET請求,如以下代碼所示:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
運作應用程式時,将以JSON格式顯示以下輸出:
[
{
"date": "2019-09-16T13:08:39.5994786-05:00",
"temperatureC": -8,
"temperatureF": 18,
"summary": "Bracing"
},
{
"date": "2019-09-17T13:08:39.5995153-05:00",
"temperatureC": 54,
"temperatureF": 129,
"summary": "Cool"
},
{
"date": "2019-09-18T13:08:39.5995162-05:00",
"temperatureC": 33,
"temperatureF": 91,
"summary": "Bracing"
},
{
"date": "2019-09-19T13:08:39.5995166-05:00",
"temperatureC": 38,
"temperatureF": 100,
"summary": "Balmy"
},
{
"date": "2019-09-20T13:08:39.599517-05:00",
"temperatureC": -3,
"temperatureF": 27,
"summary": "Sweltering"
}
]
太好了,其API的工作與我們期望的一樣,隻是它沒有給我們帶來有意義的回應。我們知道資料是響應中非常重要的部分,但是,随便吐出資料是因為JSON響應實際上并沒有太大幫助,尤其是當每個請求之間發生意外行為時。
例如,以下代碼模拟了您的代碼中可能發生的意外錯誤:
//This is just an idiot example to trigger an error
int num = Convert.ToInt32("a");
上面的代碼嘗試将包含非數字值的string轉換為integer類型,這将在運作時導緻錯誤。響應輸出看起來像這樣:
圖1:未處理的異常
你能想象那些使用你的API從響應中看到這種格式的開發人員的失望嗎?至少堆棧跟蹤資訊很有用,因為它可以使您了解錯誤的原因,但是應該進行處理,并且永遠不要讓您的API使用者看到該資訊有安全隐患。堆棧跟蹤資訊在開發階段和調試過程中絕對有幫助。在生産中,應處理此類詳細錯誤,将其記錄在某處進行分析,然後将有意義的響應傳回給消費者。
另一種情況是,當您嘗試通路 不存在的API終端時,除了著名的404 (Not Found) Http狀态代碼以外,沒有任何回應。
使用REST API時,重要的是處理異常并為API處理的所有請求傳回一緻的響應,無論成功或失敗。這使得使用API變得容易得多,而無需在用戶端上使用複雜的代碼。
AutoWrapper.Core的救援
AutoWrapper 會處理傳入的 HTTP 請求,并通過為成功和錯誤結果提供一緻的響應格式來自動為您包裝響應。目的是讓您專注于特定于業務的需求,并由包裝程式處理HTTP 響應。想象一下在執行HTTP 響應标準時,您在開發API上節省的時間。
AutoWrapper 是基于VMD.RESTApiResponseWrapper.Core的項目分支 ,旨在支援.NET Core 3.x及更高版本。對該程式包的實作進行了重構,以提供更友善的方式來使用中間件,進而增加了靈活性。
主要特點:
- 異常處理
- ModelState驗證錯誤處理(同時支援資料注釋和FluentValidation)
- 可配置的API異常
- 結果和錯誤的一緻響應格式
- 詳細的結果回複
- 詳細的錯誤響應
- 可配置的HTTP狀态碼和消息
- 添加對Swagger的支援
- 添加對請求、響應和異常的日志支援
- 在中間件中添加選項以設定ApiVersion和IsDebug屬性
TL,DR 給我看代碼
僅需幾步,您就可以将API Controller轉換為傳回一些有意義的響應,而無需付出很多開發工作。您要做的就是:
1. 從NuGet或通過CLI 下載下傳并安裝最新版本的AutoWrapper.Core:
PM> Install-Package AutoWrapper.Core -Version 1.0.1-rc
引用:
注意:目前,這是一個預發行版本,一旦.NET Core 3釋出,它将正式發行。
2.在Startup.cs中聲明以下名稱空間
using AutoWrapper;
3.在UseRouting()中間件“之前”的Startup.cs的Configure()方法中注冊下面的中間件:
app.UseApiResponseAndExceptionWrapper();
預設的API版本格式設定為“ 1.0.0.0”。如果希望為API指定其他版本格式,則可以執行以下操作:
app.UseApiResponseAndExceptionWrapper(new ApiResponseOptions { ApiVersion = "2.0" });
很簡單!現在,嘗試再次建構并運作ASP.NET Core API預設應用程式。根據我們的示例,以下是“WeatherForecastController” API 響應的樣子:
{
"message": "Request successful.",
"isError": false,
"result": [
{
"date": "2019-09-16T23:37:51.5544349-05:00",
"temperatureC": 21,
"temperatureF": 69,
"summary": "Mild"
},
{
"date": "2019-09-17T23:37:51.554466-05:00",
"temperatureC": 28,
"temperatureF": 82,
"summary": "Cool"
},
{
"date": "2019-09-18T23:37:51.554467-05:00",
"temperatureC": 21,
"temperatureF": 69,
"summary": "Sweltering"
},
{
"date": "2019-09-19T23:37:51.5544676-05:00",
"temperatureC": 53,
"temperatureF": 127,
"summary": "Chilly"
},
{
"date": "2019-09-20T23:37:51.5544681-05:00",
"temperatureC": 22,
"temperatureF": 71,
"summary": "Bracing"
}
]
}
如果您注意到了,那麼輸出現在在響應中包含一些屬性,例如message,isError以及在result屬性中包含的實際資料。
關于AutoWrapper的另一個好處是,日志記錄已經預先配置。預設情況下,.NET Core應用程式具有内置的日志記錄機制,并且包裝程式已攔截的所有請求和響應都将被自動記錄。對于此示例,它将在Visual Studio控制台視窗中顯示如下内容:
圖2:Visual Studio控制台日志
.NET Core支援與各種内置和第三方日志記錄提供程式一起使用的日志記錄API。根據您使用哪種受支援的.NET Core日志記錄提供程式以及如何配置記錄資料的位置(例如,文本檔案,Cloud等),AutoWrapper會自動為您寫入日志。
這是當您嘗試指向不存在的URL時的另一個輸出示例:
{
"isError": true,
"responseException": {
"exceptionMessage": "Request not found. The specified uri does not exist.",
"details": null,
"referenceErrorCode": null,
"referenceDocumentLink": null,
"validationErrors": null
}
}
現在注意到響應對象是如何更改的。statusCode自動設定為404。當已經發生任何意外錯誤或異常時,result屬性将自動省略,而顯示responseException屬性以顯示錯誤消息和額外資訊。
請記住,任何錯誤或異常也将被記錄。例如,如果我們再次運作以下代碼:
//This is just an idiot example to trigger an error
int num = Convert.ToInt32("a");
現在它将給您以下響應:
{
"isError": true,
"responseException": {
"exceptionMessage": "Unhandled Exception occured. Unable to process the request.",
"details": null,
"referenceErrorCode": null,
"referenceDocumentLink": null,
"validationErrors": null
}
}
控制台視窗将顯示以下内容:
圖3:Visual Studio控制台日志
預設情況下,AutoWrapper禁止顯示堆棧跟蹤資訊。如果要在開發階段從響應中檢視錯誤的實際詳細資訊,隻需将AutoWrapperOptions IsDebug設定為true:
app.UseApiResponseAndExceptionWrapper( new AutoWrapperOptions { IsDebug = true });
現在,當您再次運作該應用程式以觸發異常時,它将顯示如下内容:
{
"isError": true,
"responseException": {
"exceptionMessage": " Input string was not in a correct format.",
"details": " at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)\r\n at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)\r\n at System.Convert.ToInt32(String value)\r\n at AutoWrapperDemo.Controllers.WeatherForecastController.Get() in . . . . . . .,
"referenceErrorCode": null,
"referenceDocumentLink": null,
"validationErrors": null
}
}
注意,現在顯示了真正的異常消息及其詳細資訊。
定義自己的自定義消息
要在響應中顯示自定義消息,請使用AutoWrapper.Wrappers命名空間中的ApiResponse對象。例如,如果要在成功POST完成後顯示一條消息,則可以執行以下操作:
[HttpPost]
public async Task<ApiResponse> Post([FromBody]CreateBandDTO band)
{
//Call a method to add a new record to the database
try
{
var result = await SampleData.AddNew(band);
return new ApiResponse("New record has been created to the database", result, 201);
}
catch (Exception ex)
{
//TO DO: Log ex
throw;
}
}
成功運作代碼将為您提供以下結果:
{
"message": "New record has been created to the database",
"isError": false,
"result": 100
}
ApiResponse對象具有可以設定的以下參數:
ApiResponse(string message, object result = null, int statusCode = 200, string apiVersion = "1.0.0.0")
定義自己的Api異常
AutoWrapper還提供了可用于定義自己的異常的ApiException對象。例如,如果您想抛出自己的異常消息,則可以簡單地執行以下操作:
用于捕獲ModelState驗證錯誤
throw new ApiException(ModelState.AllErrors());
用于抛出您自己的異常消息
throw new ApiException($"Record with id: {id} does not exist.", 400);
例如,讓我們使用ModelState驗證來修改POST方法:
[HttpPost]
public async Task<ApiResponse> Post([FromBody]CreateBandDTO band)
{
if (ModelState.IsValid)
{
//Call a method to add a new record to the database
try
{
var result = await SampleData.AddNew(band);
return new ApiResponse("New record has been created to the database", result, 201);
}
catch (Exception ex)
{
//TO DO: Log ex
throw;
}
}
else
throw new ApiException(ModelState.AllErrors());
}
驗證失敗時,運作代碼将導緻如下所示:
{
"isError": true,
"responseException": {
"exceptionMessage": "Request responded with validation error(s). Please correct the specified validation errors and try again.",
"details": null,
"referenceErrorCode": null,
"referenceDocumentLink": null,
"validationErrors": [
{
"field": "Name",
"message": "The Name field is required."
}
]
}
}
檢視如何使用模型中的違規字段自動填充validationErrors屬性。
ApiException對象包含以下三個重載constructors,可用于定義異常:
ApiException(string message, int statusCode = 500, string errorCode = "", string refLink = "")
ApiException(IEnumerable<ValidationError> errors, int statusCode = 400)
ApiException(System.Exception ex, int statusCode = 500)
選項
以下屬性是可以設定的選項:
版本1.0.0
- ApiVersion
- ShowApiVersion
- ShowStatusCode
- IsDebug
1.1.0版添加
- IsApiOnly
- WrapWhenApiPathStartsWith
ShowApiVersion
如果要 在響應中顯示API版本,則可以執行以下操作:
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { ShowApiVersion = true });
預設API 版本格式設定為“ 1.0.0.0”
ApiVersion
如果您希望指定其他版本格式,則可以執行以下操作:
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions {
ShowApiVersion = true,
ApiVersion = "2.0"
});
ShowStatusCode
如果要在響應中顯示StatusCode,則可以執行以下操作:
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { ShowStatusCode = true });
IsDebug
預設情況下,AutoWrapper 禁止顯示堆棧跟蹤資訊。如果要在開發階段從響應中檢視錯誤的實際詳細資訊,隻需将設定AutoWrapperOptions IsDebug 為 true:
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { IsDebug = true });
IsApiOnly
AutoWrapper 僅可用于ASP.NET Core API項目模闆。如果要在前端項目(如Angular、MVC、React、Blazor和其他支援.NET Core的SPA架構)中組合API Controllers ,請使用此屬性啟用它。
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { IsApiOnly = false} );
WrapWhenApiPathStartsWith
如果将IsApiOnly 選項設定為false,則還可以指定API 路徑段以進行驗證。預設情況下,它設定為"/api"。如果要将其設定為其他内容,則可以執行以下操作:
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions {
IsApiOnly = false,
WrapWhenApiPathStartsWith = "/myapi"
});
當請求路徑包含WrapWhenApiPathStartsWith值時,這将激活AutoWrapper 來攔截HTTP響應。
引用:
請注意,我仍然建議您在一個單獨的項目中實作API Controllers,以重視關注點的分離,并避免為您的SPAs和API混合路由配置。
支援日志
AutoWrapper 另一個好處是,日志記錄已經預先配置。.NET Core應用程式預設情況下具有内置的日志記錄機制,并且包裝程式已攔截的所有請求和響應都将被自動記錄(由于依賴倒置!)。.NET Core支援API與各種内置和第三方日志記錄提供程式一起使用的日志記錄。根據您使用哪種受支援的.NET Core日志記錄提供程式以及如何配置記錄資料的位置(例如,文本檔案,Cloud等),AutoWrapper會自動為您寫入日志。
支援Swagger
Swagger為您的API提供了進階文檔,使開發人員可以參考您的API端點的詳細資訊并在必要時進行測試。這是非常有用的,特别是當您API是公開的并且希望許多開發人員使用它時。
AutoWrapper省略url中帶有“/swagger”的任何請求,這樣您仍然可以導航到Swagger UI以獲得您的API文檔。
總結
在本文中,我們學習了如何在您的ASP.NET Core應用程式中內建和使用AutoWrapper的核心功能。
我敢肯定,這個項目還有很多地方需要改進,請随時嘗試一下,讓我知道您的想法。
GitHub Repo
https://github.com/proudmonkey/AutoWrapper
參考文獻
- ASP.NET Core中間件
- AutoWrapper.Core
- ASP.NET Core 2.1:将VMD.RESTApiResponseWrapper.Core內建到REST API應用程式
- ASP.NET Core和Web API:用于管理異常和一緻響應的自定義包裝器