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

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

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

原文地址:https://www.cnblogs.com/tenghao510/p/11927930.html

时间: 2024-08-29 01:29:37

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

ASP.NET Core 2.2 WebApi 系列【九】使用SignalR

1.添加 SignalR 客户端库 右键点击项目->然后选择“添加” >“客户端库” 提供程序选择:unpkg ,库选择:@aspnet/[email protected] 选择“选择特定文件” ,展开“dist/browser” 文件夹,然后选择“signalr.js” 和“signalr.min.js” 选择指定位置安装即可 2.定义Hub集线器 创建MessageHub 并继承Hub.Hub类管理连接.组和消息 using System.Collections.Generic; usin

基于 ASP.NET Core 2.0 WebAPI 后台框架搭建(0) - 目录概述

概述 博主自毕业后,进公司就一直是以ASP.NET MVC 5.0 + MySQL 进行项目开发,在项目也使用了很多常用功能,如 WCF.SignalR.微信公众号API.支付宝API.Dapper等等,前端是大杂烩,如:Bootstrap.AmazeUI.EasyUI.Light7.WeUI等等.其实对于我们公司的项目来说,技术栈虽说不庞大,但五脏俱全,而且基于这一套技术,开发速度有保证.但是,作为一个有梦想的程序猿,必须与时俱进,因此无意中接触了.Net Core 2.0.听说它是开源的?它

基于 ASP.NET Core 2.0 WebAPI 后台框架搭建(1) - 依赖注入三层框架搭建

概述 本文章描述如何搭建 ASP.NET Core 2.0 WebAPI 依赖注入三层架构,为什么要加入依赖,并不是为了提供程序性能,而是为了项目间解耦,项目之间能够更加独立. 微软爸爸官方说明文档:在 ASP.NET Core 依赖注入 全面理解 ASP.NET Core 依赖注入 步骤 1. 新建解决方案,添加一个ASP.NET Core WebApi应用 2. 添加四个.Net Core类库:Entity.BLL.DAL.Common 3. 按照以下截图进行解决方案布局 4. 添加DAL层

基于 ASP.NET Core 2.0 WebAPI 后台框架搭建(4) - EF Core CodeFirst 数据库创建

概述 在 基于 ASP.NET Core 2.0 WebAPI 后台框架搭建(2) - EF Core (MySQL) CodeFirst 数据库迁移与依赖注入 一文中,我们介绍如何快速以CodeFirst快速搭建数据库,这一章,我们来完善一下创建数据库中可以添加的验证与约束. 微软爸爸官方文档:Entity Framework Core 数据库操作 (1) 数据库迁移  add-migration [任一名称,须唯一] (2) 更新数据库  update-database (3) 删除数据库迁

ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)

原文:ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用) 在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入,本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入. PS:本章将主要采用构造函数注入的方式,下一章将继续分享如何使之能够同时支持属性注入的方式. 约定: 1.仓储层接口都以“I”开头,以“Repos

ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射

原文:ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射 本章主要简单介绍下在ASP.NET Core中如何使用AutoMapper进行实体映射.在正式进入主题之前我们来看下几个概念: 1.数据库持久化对象PO(Persistent Object):顾名思义,这个对象是用来将我们的数据持久化到数据库,一般来说,持久化对象中的字段会与数据库中对应的 table 保持一致. 2.视图对象VO(View Object):视图对象 V

ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await

原文:ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await PS:异步编程的本质就是新开任务线程来处理. 约定:异步的方法名均以Async结尾. 实际上呢,异步编程就是通过Task.Run()来实现的. 了解线程的人都知道,新开一个线程来处理事务这个很常见,但是在以往是没办法接收线程里面返回的值的.所以这时候就该await出场了,await从字面意思不难理解,就是等待的意思. 执行await的方法必须是async修饰的,并且是Task

ASP.NET Core 1.1 静态文件、路由、自定义中间件、身份验证简介

概述 之前写过一篇关于<ASP.NET Core 1.0 静态文件.路由.自定义中间件.身份验证简介>的文章,主要介绍了ASP.NET Core中StaticFile.Middleware.CustomizeMiddleware和Asp.NetCore Identity.但是由于所有的ASP.NET Core的版本有些老,所以,此次重写一次.使用最新的ASP.NET Core 1.1版本.对于ASP.NET Core 1.1 Preview 1会在以后的文章中介绍 目录 使用静态文件 使用路由

asp.net core 2.0 webapi集成signalr

在博客园也很多年了,一直未曾分享过什么东西,也没有写过博客,但自己也是汲取着博客园的知识成长的: 这两天想着不能这么无私,最近.NET CORE貌似挺流行的,闲来无事也自己搞了个asp.net core signalr 博客园里面也有人在.net core 2.0下面集成了signalr,但是是集成在同一个项目里面的,但是大家都知道我们很多的项目都是分离的: 而且signalr涉及到连接数和内存资源的占用问题,如果都集成在一个项目里面当访问量多大的时候容易造成网站访问缓慢,具体原因就不多说了 所