ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)

眉间皱痕 提交于 2019-11-25 18:51:21

现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间。我们需要把所有接口及异常错误信息都返回一定的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;                 });

参考:https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.mvc.apibehavioroptions?view=aspnetcore-2.2

自定义过滤器

创建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));                     };                 });

两种验证方法效果是一致的

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!