• ASP.NET Core AutoWrapper 自定义响应输出


    前言

    AutoWrapper是一个简单可自定义全局异常处理程序和ASP.NET Core API响应的包装。他使用ASP.NET Core middleware拦截传入的HTTP请求,并将最后的结果使用统一的格式来自动包装起来.目的主要是让我们更多的关注业务特定的代码要求,并让包装器自动处理HTTP响应。这可以在构建API时加快开发时间,同时为HTTP响应试试我们统一的标准。

    安装

    AutoWrapper.Core从NuGet或通过CLI下载并安装

    PM> Install-Package AutoWrapper.Core 
    

    在Startup.cs Configure方法中注册以下内容,但是切记要放在UseRouting前

    app.UseApiResponseAndExceptionWrapper();  
    

    启动属性映射

    默认情况下AutoWrapper将在成功请求成功时输出以下格式:

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

    如果说不喜欢默认属性命名方式,那么我们可以通过AutoWrapperPropertyMap属性进行映射为我们需要指定的任何名称。例如我么可以将result属性的名称更改为data。如下所示

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

    然后将MapResponseObject类传递给AutpWrapper middleware

    app.UseApiResponseAndExceptionWrapper<MapResponseObject>();  
    

    通过映射重新请求后,现在影响格式如下所示

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

    可以从中看出result属性已经更换为data属性了

    默认情况下AutoWrapper发生异常时将吐出以下响应格式

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

    而且如果在AutoWrapperOptions中设置了IsDebug,则将产生带有堆栈跟踪信息的类似信息

    {
        "isError": true,
        "responseException": {
            "exceptionMessage": " Input string was not in a correct format.",
            "details": "   at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)rn   at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)rn …"
        }
    }
    

    如果想将某些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)rn   at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)rn …"
        }
    }
    

    请注意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

  • 相关阅读:
    ORM选型对比
    使用vue和web3创建你的第一个以太坊APP
    二维码转账
    mysql分布式技术
    MyCAT简易入门
    交易流程
    【Unix网络编程】chapter3套接字编程简介
    【Unix网络编程】chapter1简介
    《从你的全世界路过》
    OpenGL 多线程共享纹理
  • 原文地址:https://www.cnblogs.com/zhangxiaoxia/p/13131653.html
Copyright © 2020-2023  润新知