ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)
程序员文章站
2022-07-02 12:25:26
现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间。我们需要把所有接口及异常错误信息都返回一定的Json格式,有利于前端处理,从而提高了工作效率。 一、准备工作 定义响应实体类 /// /// 响应实体类 ///
现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间。我们需要把所有接口及异常错误信息都返回一定的json格式,有利于前端处理,从而提高了工作效率。
一、准备工作
定义响应实体类
/// <summary> /// 响应实体类 /// </summary> public class resultmodel { /// <summary> /// 状态码 /// </summary> public int returncode { get; set; } /// <summary> /// 内容 /// </summary> public object data { get; set; } /// <summary> /// 错误信息 /// </summary> public string errormessage { get; set; } /// <summary> /// 是否成功 /// </summary> public bool issuccess { get; set; } }
修改controller层
在controller层处理业务请求,new 一个resultmodel 对象,返回给前端。
/// <summary> /// 查询用户 /// </summary> /// <returns></returns> [route("getuser")] [httpget] public resultmodel getuser() { var data = _userrepository.getall().tolist(); return new resultmodel() { data = data, errormessage = null, issuccess = true, returncode = 200 }; }
这样需要每个方法都需要重新new一个resultmodel 对象,感觉有点代码冗余。
我们只需要加几个静态方法,每个方法返回都是resultmodel对象,成功的时候调用resultmodel.ok,失败的时候调用resultmodel.error即可。
优化
添加两个静态方法
/// <summary> /// 成功 /// </summary> /// <param name="data">返回数据</param> /// <returns></returns> public static resultmodel ok(object data) { return new resultmodel { data = data, errormessage = null, issuccess = true, returncode = 200 }; } /// <summary> /// 失败 /// </summary> /// <param name="str">错误信息</param> /// <param name="code">状态码</param> /// <returns></returns> public static resultmodel error(string str,int code) { return new resultmodel { data = null, errormessage = str, issuccess = false, returncode = code }; }
修改控制器
/// <summary> /// 查询用户 /// </summary> /// <returns></returns> [route("getuser")] [httpget] public resultmodel getuser() { var data = _userrepository.getall().tolist(); return resultmodel.ok(data); }
二、全局异常处理
通过全局异常处理,任何错误信息都会被拦截,返回统一格式。
定义全局异常处理中间件
using system; using system.threading.tasks; using microsoft.aspnetcore.http; using netcorewebapi.util; using newtonsoft.json; namespace netcorewebapi.filter { /// <summary> /// 处理全局信息中间件 /// </summary> public class exceptionmiddleware { /// <summary> /// 处理http请求的函数。 /// </summary> private readonly requestdelegate _next; /// <summary> /// 构造函数 /// </summary> /// <param name="next"></param> public exceptionmiddleware(requestdelegate next) { _next = next; } public async task invoke(httpcontext context) { try { //抛给下一个中间件 await _next(context); } catch (exception ex) { await writeexceptionasync(context, ex); } finally { await writeexceptionasync(context, null); } } private async task writeexceptionasync(httpcontext context, exception exception) { if (exception != null) { var response = context.response; var message = exception.innerexception == null ? exception.message : exception.innerexception.message; response.contenttype = "application/json"; await response.writeasync(jsonconvert.serializeobject(resultmodel.error(message, 400))).configureawait(false); } else { var code = context.response.statuscode; switch (code) { case 200: return; case 204: return; case 401: context.response.contenttype = "application/json"; await context.response.writeasync(jsonconvert.serializeobject(resultmodel.error("token已过期,请重新登录.", code))).configureawait(false); break; default: context.response.contenttype = "application/json"; await context.response.writeasync(jsonconvert.serializeobject(resultmodel.error("未知错误", code))).configureawait(false); break; } } } } }
注册中间件
在startup.cs启动类的configure方法中添加usemiddleware方法
//异常处理中间件 app.usemiddleware(typeof(exceptionmiddleware));
三、验证实体模型
有两种方式:
1.使用自定义过滤器
2.使用默认自带的400模型验证,需要在控制器上面加上【apicontroller】,这种方式优先级比较高,如果需要自定义模型验证,则需要先关闭默认的模型验证
【apicontroller】
自动推断参数绑定:可以省略[frombody]等参数特性
自动模型验证:自动验证模型是否合法
参考:https://blog.csdn.net/sd7o95o/article/details/81844154
services.addmvc(e => { e.filters.add<checkmodel>(); }) .setcompatibilityversion(compatibilityversion.version_2_2) .configureapibehavioroptions(e => { //关闭默认模型验证 e.suppressmodelstateinvalidfilter = true; });
参考:
自定义过滤器
创建checkmodel过滤器继承actionfilterattribute抽象类,重写其中需要的方法
using microsoft.aspnetcore.mvc; using microsoft.aspnetcore.mvc.filters; using netcorewebapi.util; namespace netcorewebapi.filter { /// <summary> /// 验证实体对象是否合法 /// </summary> public class checkmodel : actionfilterattribute { /// <summary> /// action 调用前执行 /// </summary> /// <param name="actioncontext"></param> public override void onactionexecuting(actionexecutingcontext actioncontext) { if (!actioncontext.modelstate.isvalid) { //初始化返回结果 var result = new resultmodel { issuccess = false, returncode = 400 }; foreach (var item in actioncontext.modelstate.values) { foreach (var error in item.errors) { result.errormessage += error.errormessage + "|"; } } actioncontext.result = new badrequestobjectresult(result); } } /// <summary> /// action 方法调用后,result 方法调用前执行 /// </summary> /// <param name="context"></param> public override void onactionexecuted(actionexecutedcontext context) {
} } }
将写的过滤器注册到全局
services.addmvc(e => { //注册过滤器 e.filters.add<checkmodel>(); }) .setcompatibilityversion(compatibilityversion.version_2_2) .configureapibehavioroptions(e => { //关闭默认模型验证 e.suppressmodelstateinvalidfilter = true; });
创建userdto
using system.componentmodel.dataannotations; namespace netcorewebapi.repository.dto { /// <summary> /// 用户传输对象 /// </summary> public class userdto { /// <summary> /// 用户id /// </summary> [stringlength(32, errormessage = "{0}最多{1}个字符"), display(name = "用户id")] public string userid { get; set; } /// <summary> /// 用户名 /// </summary> [stringlength(20, errormessage = "{0}最多{1}个字符"), display(name = "用户名")] public string username { get; set; } /// <summary> /// 邮箱 /// </summary> [stringlength(30, errormessage = "{0}最多{1}个字符"), display(name = "邮箱")] public string email { get; set; } } }
测试
默认模型验证
services.addmvc(e => { //注册过滤器 //e.filters.add<checkmodel>(); }) .setcompatibilityversion(compatibilityversion.version_2_2) .configureapibehavioroptions(e => { ////关闭默认模型验证 //e.suppressmodelstateinvalidfilter = true; e.invalidmodelstateresponsefactory = actioncontext => { //获取验证失败的模型字段 var errors = actioncontext.modelstate .where(e1 => e1.value.errors.count > 0) .select(e1 => e1.value.errors.first().errormessage) .tolist(); var str = string.join("|", errors); return new badrequestobjectresult(resultmodel.error(str, 400)); }; });
两种验证方法效果是一致的