ASP.NET Core AutoWrapper 自定義響應輸出

前言

AutoWrapper是一個簡單可自定義全局異常處理程序和ASP.NET Core API響應的包裝。他使用ASP.NET Core middleware攔截傳入的HTTP請求,並將最後的結果使用統一的格式來自動包裝起來.目的主要是讓咱們更多的關注業務特定的代碼要求,並讓包裝器自動處理HTTP響應。這能夠在構建API時加快開發時間,同時爲HTTP響應試試咱們統一的標準。git

安裝

AutoWrapper.Core從NuGet或經過CLI下載並安裝github

PM> Install-Package AutoWrapper.Core

在Startup.cs Configure方法中註冊如下內容,可是切記要放在UseRouting前api

app.UseApiResponseAndExceptionWrapper();

啓動屬性映射

默認狀況下AutoWrapper將在成功請求成功時輸出如下格式:架構

{
    "message": "Request successful.",
    "isError": false,
    "result": [
      {
        "id": 7002,
        "firstName": "Vianne",
        "lastName": "Durano",
        "dateOfBirth": "2018-11-01T00:00:00"
      }
    ]
}

若是說不喜歡默認屬性命名方式,那麼咱們能夠經過AutoWrapperPropertyMap屬性進行映射爲咱們須要指定的任何名稱。例如我麼能夠將result屬性的名稱更改成data。以下所示app

public class MapResponseObject  
{
    [AutoWrapperPropertyMap(Prop.Result)]
    public object Data { get; set; }
}

而後將MapResponseObject類傳遞給AutpWrapper middlewareasync

app.UseApiResponseAndExceptionWrapper<MapResponseObject>();

經過映射從新請求後,如今影響格式以下所示測試

{
    "message": "Request successful.",
    "isError": false,
    "data": {
        "id": 7002,
        "firstName": "Vianne",
        "lastName": "Durano",
        "dateOfBirth": "2018-11-01T00:00:00"
    }
}

能夠從中看出result屬性已經更換爲data屬性了this

默認狀況下AutoWrapper發生異常時將吐出如下響應格式code

{
    "isError": true,
    "responseException": {
        "exceptionMessage": "Unhandled Exception occurred. Unable to process the request."
    }
}

並且若是在AutoWrapperOptions中設置了IsDebug,則將產生帶有堆棧跟蹤信息的相似信息orm

{
    "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 …"
    }
}

若是想將某些APIError屬性名稱更改成其餘名稱,只須要在如下代碼中添加如下映射MapResponseObject

public class MapResponseObject  
{
    [AutoWrapperPropertyMap(Prop.ResponseException)]
    public object Error { get; set; }

    [AutoWrapperPropertyMap(Prop.ResponseException_ExceptionMessage)]
    public string Message { get; set; }

    [AutoWrapperPropertyMap(Prop.ResponseException_Details)]
    public string StackTrace { get; set; }
}

經過以下代碼來模擬錯誤

int num = Convert.ToInt32("10s");

如今映射後的輸出以下所示

{
    "isError": true,
    "error": {
        "message": " Input string was not in a correct format.",
        "stackTrace": "   at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)\r\n   at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)\r\n …"
    }
}

請注意APIError如今根據MapResponseObject類中定義的屬性更改了模型的默認屬性。

咱們能夠自由的選擇映射任何屬性,下面是映射屬性相對應的列表

[AutoWrapperPropertyMap(Prop.Version)]
[AutoWrapperPropertyMap(Prop.StatusCode)]
[AutoWrapperPropertyMap(Prop.Message)]
[AutoWrapperPropertyMap(Prop.IsError)]
[AutoWrapperPropertyMap(Prop.Result)]
[AutoWrapperPropertyMap(Prop.ResponseException)]
[AutoWrapperPropertyMap(Prop.ResponseException_ExceptionMessage)]
[AutoWrapperPropertyMap(Prop.ResponseException_Details)]
[AutoWrapperPropertyMap(Prop.ResponseException_ReferenceErrorCode)]
[AutoWrapperPropertyMap(Prop.ResponseException_ReferenceDocumentLink)]
[AutoWrapperPropertyMap(Prop.ResponseException_ValidationErrors)]
[AutoWrapperPropertyMap(Prop.ResponseException_ValidationErrors_Field)]
[AutoWrapperPropertyMap(Prop.ResponseException_ValidationErrors_Message)]

自定義錯誤架構

AutoWrapper還提供了一個APIException可用於定義本身的異常的對象,若是想拋出本身的異常消息,則能夠簡單地執行如下操做

throw new ApiException("Error blah", 400, "511", "http://blah.com/error/511");

默認輸出格式以下所示

{
    "isError": true,
    "responseException": {
        "exceptionMessage": "Error blah",
        "referenceErrorCode": "511",
        "referenceDocumentLink": "http://blah.com/error/511"
    }
}

固然咱們能夠自定義錯誤格式

public class MapResponseObject  
{
    [AutoWrapperPropertyMap(Prop.ResponseException)]
    public object Error { get; set; }
}

public class Error  
{
    public string Message { get; set; }

    public string Code { get; set; }
    public InnerError InnerError { get; set; }

    public Error(string message, string code, InnerError inner)
    {
        this.Message = message;
        this.Code = code;
        this.InnerError = inner;
    }

}

public class InnerError  
{
    public string RequestId { get; set; }
    public string Date { get; set; }

    public InnerError(string reqId, string reqDate)
    {
        this.RequestId = reqId;
        this.Date = reqDate;
    }
}

而後咱們能夠經過以下代碼進行引起咱們錯誤

throw new ApiException(  
      new Error("An error blah.", "InvalidRange",
      new InnerError("12345678", DateTime.Now.ToShortDateString())
));

輸出格式以下所示

{
    "isError": true,
    "error": {
        "message": "An error blah.",
        "code": "InvalidRange",
        "innerError": {
            "requestId": "12345678",
            "date": "10/16/2019"
        }
    }
}

使用自定義API響應格式

若是映射知足不了咱們的需求。而且咱們須要向API響應模型中添加其餘屬性,那麼咱們如今能夠自定義本身的格式類,經過設置UseCustomSchema爲true來實現,代碼以下所示

app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { UseCustomSchema = true });

如今假設咱們想在主API中響應中包含一個屬性SentDate和Pagination對象,咱們可能但願將API響應模型定義爲如下格式

public class MyCustomApiResponse  
{
    public int Code { get; set; }
    public string Message { get; set; }
    public object Payload { get; set; }
    public DateTime SentDate { get; set; }
    public Pagination Pagination { get; set; }

    public MyCustomApiResponse(DateTime sentDate, object payload = null, string message = "", int statusCode = 200, Pagination pagination = null)
    {
        this.Code = statusCode;
        this.Message = message == string.Empty ? "Success" : message;
        this.Payload = payload;
        this.SentDate = sentDate;
        this.Pagination = pagination;
    }

    public MyCustomApiResponse(DateTime sentDate, object payload = null, Pagination pagination = null)
    {
        this.Code = 200;
        this.Message = "Success";
        this.Payload = payload;
        this.SentDate = sentDate;
        this.Pagination = pagination;
    }

    public MyCustomApiResponse(object payload)
    {
        this.Code = 200;
        this.Payload = payload;
    }

}

public class Pagination  
{
    public int TotalItemsCount { get; set; }
    public int PageSize { get; set; }
    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
}

經過以下代碼片斷進行測試結果

public async Task<MyCustomApiResponse> Get()  
{
    var data = await _personManager.GetAllAsync();

    return new MyCustomApiResponse(DateTime.UtcNow, data,
        new Pagination
        {
            CurrentPage = 1,
            PageSize = 10,
            TotalItemsCount = 200,
            TotalPages = 20
        });

}

運行後會獲得以下影響格式

{
    "code": 200,
    "message": "Success",
    "payload": [
        {
            "id": 1,
            "firstName": "Vianne",
            "lastName": "Durano",
            "dateOfBirth": "2018-11-01T00:00:00"
        },
        {
            "id": 2,
            "firstName": "Vynn",
            "lastName": "Durano",
            "dateOfBirth": "2018-11-01T00:00:00"
        },
        {
            "id": 3,
            "firstName": "Mitch",
            "lastName": "Durano",
            "dateOfBirth": "2018-11-01T00:00:00"
        }
    ],
    "sentDate": "2019-10-17T02:26:32.5242353Z",
    "pagination": {
        "totalItemsCount": 200,
        "pageSize": 10,
        "currentPage": 1,
        "totalPages": 20
    }
}

可是從這裏要注意一旦咱們對API響應進行自定義,那麼就表明咱們徹底控制了要格式化數據的方式,同時丟失了默認API響應的某些選項配置。可是咱們仍然能夠利用ApiException()方法引起用戶定義的錯誤消息
以下所示

[Route("{id:long}")]
[HttpPut]
public async Task<MyCustomApiResponse> Put(long id, [FromBody] PersonDTO dto)  
{
    if (ModelState.IsValid)
    {
        try
        {
            var person = _mapper.Map<Person>(dto);
            person.ID = id;

            if (await _personManager.UpdateAsync(person))
                return new MyCustomApiResponse(DateTime.UtcNow, true, "Update successful.");
            else
                throw new ApiException($"Record with id: {id} does not exist.", 400);
        }
        catch (Exception ex)
        {
            _logger.Log(LogLevel.Error, ex, "Error when trying to update with ID:{@ID}", id);
            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.",
        "validationErrors": [
            {
                "field": "FirstName",
                "message": "'First Name' must not be empty."
            }
        ]
    }
}

Reference

https://github.com/proudmonkey/AutoWrapper

相關文章
相關標籤/搜索