天天看點

AutoWrapper:通過有意義的響應來美化您的ASP.NET Core API介紹 預設的ASP.NET Core API響應AutoWrapper.Core的救援TL,DR 給我看代碼定義自己的自定義消息定義自己的Api異常選項支援日志支援Swagger總結參考文獻

目錄

介紹 

預設的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類型,這将在運作時導緻錯誤。響應輸出看起來像這樣:

AutoWrapper:通過有意義的響應來美化您的ASP.NET Core API介紹 預設的ASP.NET Core API響應AutoWrapper.Core的救援TL,DR 給我看代碼定義自己的自定義消息定義自己的Api異常選項支援日志支援Swagger總結參考文獻

圖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控制台視窗中顯示如下内容:

AutoWrapper:通過有意義的響應來美化您的ASP.NET Core API介紹 預設的ASP.NET Core API響應AutoWrapper.Core的救援TL,DR 給我看代碼定義自己的自定義消息定義自己的Api異常選項支援日志支援Swagger總結參考文獻

圖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
    }
}
           

控制台視窗将顯示以下内容:

AutoWrapper:通過有意義的響應來美化您的ASP.NET Core API介紹 預設的ASP.NET Core API響應AutoWrapper.Core的救援TL,DR 給我看代碼定義自己的自定義消息定義自己的Api異常選項支援日志支援Swagger總結參考文獻

圖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:用于管理異常和一緻響應的自定義包裝器

繼續閱讀