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

这样需要每个方法都需要重新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 = };
}
/// <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, ))).ConfigureAwait(false);
}
else
{
var code = context.Response.StatusCode;
switch (code)
{
case :
return;
case :
return;
case :
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 = };
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(, ErrorMessage = "{0}最多{1}个字符"), Display(Name = "用户Id")]
public string UserId { get; set; }
/// <summary>
/// 用户名
/// </summary>
[StringLength(, ErrorMessage = "{0}最多{1}个字符"), Display(Name = "用户名")]
public string UserName { get; set; }
/// <summary>
/// 邮箱
/// </summary>
[StringLength(, 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 > )
.Select(e1 => e1.Value.Errors.First().ErrorMessage)
.ToList();
var str = string.Join("|", errors);
return new JsonResult(ResultModel.Error(str, ));
};
});

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

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

  1. ASP.NET Core 2.2 WebApi 系列【九】使用SignalR (作者:tenghao510 ) 学习及内容补充

    原文地址:  ASP.NET Core 2.2 WebApi 系列[九]使用SignalR 今天,看到了大牛的这篇博文,  发了一下评论, 我很惊喜, 没想到他很快就回复了我,  而且通过QQ帮助了S ...

  2. ASP.NET Core 3.0 WebApi 系列【1】创建ASP.NET Core WebApi 项目

    目录 写在前面 一.运行环境 二.项目搭建 三.测试 API 四.基础知识 五.写在最后 写在前面 C#语言可以创建RESTful服务,被称作WebApi.在这里总结学习使用支持创建.读取.更新.删除 ...

  3. ASP.NET Core 2.2 WebApi 系列【一】搭建ASP.NET Core WebApi项目

    一.步骤 从“文件”菜单中选择“新建”>“项目” . 选择“ASP.NET Core Web 应用程序”模板,再单击“下一步” . 将项目命名为 NetCoreWebApi,然后单击“创建” . ...

  4. ASP.NET Core 3.0 WebApi 系列【2】.Net Core 3.0+ CodeFirst + MySql 实现数据的迁移

    写在前面 在前一小节中,我们创建了基于RESFULL风格的服务.这个小节,尝试使用CodeFirst+MySql 的方式实现数据迁移. 一.开发环境 [1]运行环境:win10 家庭版 [2]开发工具 ...

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

    1.添加 SignalR 客户端库 右键点击项目->然后选择“添加” >“客户端库” 提供程序选择:unpkg ,库选择:@aspnet/signalr@1.1.4 选择“选择特定文件” ...

  6. ASP.NET Core 2.2 WebApi 系列【七】泛型仓储模式和工作单元

    在之前的泛型仓储模式实现中,每个增删改都调用了SaveChanges方法,导致每次更新都提交了事务. 在实际开发过程中,我们经常遇到同时操作多张表数据,那么按照之前的写法,对数据库提交了多次操作,开启 ...

  7. ASP.NET Core 2.2 WebApi 系列【六】泛型仓储模式

    为什么要使用泛型仓储?好处是? 前两章在autofac注入的时候,用的User类作为例子,写了增删改查四个接口,也就是仓储的GRUD. 当我们再添加一个实体(比如Student)时,StudentRe ...

  8. ASP.NET Core 2.2 WebApi 系列【五】MiniProfiler与Swagger集成

    MiniProfiler 是一款性能分析的轻量级程序,可以基于action(request)记录每个阶段的耗时时长,还是可以显示访问数据库时的SQL(支持EF.EF Code First)等 一.安装 ...

  9. ASP.NET Core 2.2 WebApi 系列【四】集成Swagger

    Swagger 是一款自动生成在线接口文档+功能测试功能软件 一.安装程序包 通过管理 NuGet 程序包安装,搜索Swashbuckle.AspNetCore 二.配置 Swagger 将 Swag ...

随机推荐

  1. 《Java基础知识》Java 运算符

    计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量.我们可以把运算符分成以下几组: 算术运算符 关系运算符 位运算符 逻辑运算符 赋值运算符 其他运 ...

  2. Linux服务器部署.Net Core笔记:六、安装MySQL

    接下来我们在 Centos7 系统下使用 yum 命令安装 MySQL,需要注意的是 CentOS 7 版本中 MySQL数据库已从默认的程序列表中移除,所以在安装前我们需要先去官网下载 Yum 资源 ...

  3. Supermap/Cesium 开发心得----定位

    SuperMap的WebGL是基于开源JS库Cesium做的修改而形成的产品,理论上用起来大同小异,如果在有不一样的地方再看,基本上还是与Cesium的接口名称和结构是一样的. 定位方法有基于Cesi ...

  4. Python—类和实例对象

    面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可 ...

  5. Shell—详解$( )、$(( ))、``与${ }的区别

    https://www.jianshu.com/p/2237f029c385 https://www.cnblogs.com/chenpython123/p/11052276.html https:/ ...

  6. Matlab非线性规划

    非线性规划 在matlab非线性规划数学模型可以写成一下形式: \[ minf(x)\\ s.t.\begin{cases} Ax \le B \\ Aeq·x = Beq\\ C(x) \le 0\ ...

  7. 从eslint规则学点JavaScript知识

    在知识星球陆续发了些eslint小知识,意在通过eslint知识去了解规则内部的原理,汇总在此: prefer-const 含义:始终用const定义你从不修改的变量,如果会被修改则使用let,永不使 ...

  8. Spring Boot启动提示:org.apache.catalina.LifecycleException: A child container failed during start

    一.问题回顾 最近在做一个新项目,从git上下载导入idea后,启动项目,但是报了如下错误: java.util.concurrent.ExecutionException: org.apache.c ...

  9. C#基础之多线程与异步

    1.基本概念 多线程与异步是两个不同概念,之所以把这两个放在一起学习,是因为这两者虽然有区别,但也有一定联系. 多线程是一个技术概念,相对于单线程而言,多线程是多个单线程同时处理逻辑.例如,假如说一个 ...

  10. 爬虫(二):抓包工具Fiddler

    1. 抓包工具Fiddler 1.1 Fiddler下载与安装 最简单的方法,打开百度,搜索fiddler下载. 下载完毕解压即可,此版本为绿色版. 点击这个即可运行抓包软件. 1.2 Fiddler ...