Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 807|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-4-11 13:35:11 | 显示全部楼层 |阅读模式

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

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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-18 18:21 , Processed in 0.069448 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表