余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

在ASP.NET Core中使用 AutoWrapper 自定义响应输出

xiyangw 2022-11-24 16:50 45 浏览 0 评论


前言

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)\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(#34;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

相关推荐

数控系统常见术语详解,机加工人士必备资料
数控系统常见术语详解,机加工人士必备资料

增量编码器(Incrementpulsecoder)回转式位置测量元件,装于电动机轴或滚珠丝杠上,回转时发出等间隔脉冲表示位移量。由于没有记忆元件,故不能准...

2023-09-24 17:42 xiyangw

功、功率、扭矩的关系

功=功率×时间work=power×timeW=P×T功=力×距离work=force×lengthW=F×LP×T=F×LP=F×L/T=F×V(velocity)具体到电机输出轴上,圆...

Wi-Fi协议(802.11 )常见专业术语汇总
Wi-Fi协议(802.11 )常见专业术语汇总

Wi-Fi协议(802.11)常见专业术语汇总AP(Accesspoint的简称,即访问点,接入点):是一个无线网络中的特殊节点,通过这个节点,无线网络中的...

2023-09-24 17:41 xiyangw

不需要策略模式也能避免满屏if/else
不需要策略模式也能避免满屏if/else

满屏if/elsejava复制代码publicstaticvoidmain(String[]args){inta=1;if...

2023-09-24 17:41 xiyangw

喜极而泣,我终于干掉了该死的 if-else
喜极而泣,我终于干掉了该死的 if-else

推荐阅读:面试淘宝被Tomcat面到“自闭”,学习这份文档之后“吊打”面试官刷完spring+redis+负载均衡+netty+kafka面试题,再去面试BAT...

2023-09-24 17:40 xiyangw

Python中使用三元运算符简化if-else语句
Python中使用三元运算符简化if-else语句

Python是一种极简主义的编程语言,相比其他编程语言,在多个地方简化了代码的写法,可以让我们用更少的时间更简洁地完成工作。以赋值运算符为例:a=a+b简化...

2023-09-24 17:40 xiyangw

雅思课堂 | 雅思口语写作句型第二讲
雅思课堂 | 雅思口语写作句型第二讲

纯干货,无废话用最少的时间学最制胜的内容!泡图书馆泡不过学霸?碎片时间也能弯道超车!向着雅思8分行动起来吧!雅思口语写作句型1.Ipreferseeing...

2023-09-24 17:39 xiyangw

设计模式(三)——简单的状态模式代替if-else
设计模式(三)——简单的状态模式代替if-else

博主将会针对Java面试题写一组文章,包括J2ee,SQL,主流Web框架,中间件等面试过程中面试官经常问的问题,欢迎大家关注。一起学习,一起成长。前言大多数开...

2023-09-24 17:38 xiyangw

如何优化代码中大量的if/else,switch/case?

前言随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗?正文使用枚举这里我们简单的定义一...

优秀程序员早就学会用“状态模式”代替if-else了
优秀程序员早就学会用“状态模式”代替if-else了

2020年已经进入倒计时了,大家立好的flag完成了吗?2020实“鼠”不易,希望2021可以“牛”转乾坤。简介状态模式是行为型设计模式的一种。其设计理念是当对...

2023-09-24 17:37 xiyangw

用Select Case语句对执行多条件进行控制
用Select Case语句对执行多条件进行控制

今日的内容是"VBA之EXCEL应用"的第六章"条件判断语句(If...Then...Else)在VBA中的利用"。这讲是第三节...

2023-09-24 17:37 xiyangw

c#入门教程(四)条件判断if else

条件判断,是编程里常用的判断语句,比如某个代码如果满足条件就执行a代码块否则就执行b代码块。案例1:inti=2*5;if(a>0){执行a代码块}elseif(a<0){执行b代码块...

每日学编程之JAVA(十一)—条件语句(if……else)

一个if语句包含一个布尔表达式和一条或多条语句。如果布尔表达式的值为true,则执行if语句中的代码块,否则执行if语句块后面的代码。if语句后面可以跟else语句,当if语句...

不需要策略模式也能避免满屏if/else

除了使用策略模式以外,还可以使用其他设计模式来避免满屏if/else的问题。以下是一些可能的解决方案:工厂模式:将if/else语句移到工厂类中,由工厂类负责创建对象。这样可以将if/else语句从客...

围绕ifelse与业务逻辑的那些梗
围绕ifelse与业务逻辑的那些梗

ifelse很重要,几乎是程序员编程核心,业务逻辑与规则也通过ifelse体现出来,语句简单但是背后文章很大,先看几则幽默图:1.也许默认使用returnf...

2023-09-24 17:36 xiyangw

取消回复欢迎 发表评论: