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响应模型中添加其余属性,那么咱们如今能够自定义本身的格式类,经过设置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." } ] } }