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)\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
上一篇: 她抽的是我
推荐阅读
-
ASP.NET Core中自定义路由约束的实现
-
asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
-
asp.net core 自定义基于 HttpContext 的 Serilog Enricher
-
ASP.net中Core自定义View查找位置的实例代码
-
ASP.NET Core自定义本地化教程之从文本文件读取本地化字符串
-
解决asp.net core在输出中文时乱码的问题
-
基于Asp.Net Core MVC和AdminLTE的响应式管理后台之侧边栏处理
-
ASP.NET Core Web 应用程序开发期间部署到IIS自定义主机域名并附加到进程调试
-
在ASP.NET Core中显示自定义的错误页面
-
详解Asp.Net Core 2.1+的视图缓存(响应缓存)