ASP.NET Core实现自定义WebApi模型验证详解

Framework时代  

在Framework时代,我们一般进行参数验证的时候,以下代码是非常常见的

[HttpPost]
  public async Task<JsonResult> SaveNewCustomerAsnyc(AddCustomerInput input)
  {
   if (!ModelState.IsValid)
   {
    return Json(Result.FromCode(ResultCode.InvalidParams));
   }

   .....
  }

  或者高级一点是实现IActionFilter进行拦截,如下:  

public class ApiValidationFilter : IActionFilter
 {
  public bool AllowMultiple => false;

  public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
  {
   var method = actionContext.ActionDescriptor.GetMethodInfoOrNull();
   if (method == null)
   {
    return await continuation();
   }   

   if (!actionContext.ModelState.IsValid)
   {
    var error = actionContext.ModelState.GetValidationSummary();
    var result = Result.FromError($"参数验证不通过:{error}", ResultCode.InvalidParams);
    return actionContext.Request.CreateResponse(result);
   }

   return await continuation();
  }
}
public static class ModelStateExtensions
 {
  /// <summary>
  /// 获取验证消息提示并格式化提示
  /// </summary>
  public static string GetValidationSummary(this ModelStateDictionary modelState, string separator = "\r\n")
  {
   if (modelState.IsValid) return null;

   var error = new StringBuilder();

   foreach (var item in modelState)
   {
    var state = item.Value;
    var message = state.Errors.FirstOrDefault(p => !string.IsNullOrWhiteSpace(p.ErrorMessage))?.ErrorMessage;
    if (string.IsNullOrWhiteSpace(message))
    {
     message = state.Errors.FirstOrDefault(o => o.Exception != null)?.Exception.Message;
    }
    if (string.IsNullOrWhiteSpace(message)) continue;

    if (error.Length > 0)
    {
     error.Append(separator);
    }

    error.Append(message);
   }

   return error.ToString();
  }
 }

然后在启动项把这个拦截注册进来使用即可 

.Net Core时代  

自动模型状态验证

在.Net Core的时代中,框架会帮你自动验证model的state,也就是ModelState。框架会为你自动注册ModelStateInvalidFilter,这个会运行在OnActionExecuting事件里面。

基于现有框架的代码编写的话,所以我们不再需要在业务中耦合这样的模型判断代码,系统内部会检查ModelState是否为Valid,如果为InValid会直接返回400 BadRequest,这样就没有必要执行后面的代码,提高效率。因此,操作方法中不再需要以下代码: 

if (!ModelState.IsValid)
{
  return BadRequest(ModelState);
}

问题引入  

在我们的真实开发中,当我们碰到参数验证没通过400错误时,我们希望的是后台返回一个可理解的Json结果返回,而不是直接在页面返回400错误。所以我们需要替换掉默认的BadRequest响应结果,把结果换成我们想要的Json结果返回。

自定义 BadRequest 响应

我们如何改变 ASP.NET Core WEB API 模型验证的默认行为呢?具体的做法是在通过Startup的ConfigureServices方法配置ApiBehaviorOptions来实现,先来看一下这个类。 

public class ApiBehaviorOptions
  {
    public Func<ActionContext, IActionResult> InvalidModelStateResponseFactory { get; set; }

    public bool SuppressModelStateInvalidFilter { get; set; }

    public bool SuppressInferBindingSourcesForParameters { get; set; }

    public bool SuppressConsumesConstraintForFormFileParameters { get; set; }
  }

所有bool类型的属性默认都是false。

方案一

当 SuppressModelStateInvalidFilter 属性设置为 true 时,会禁用默认行为  

public void ConfigureServices(IServiceCollection services)
    {
      services
         .AddMvc()
        .AddXmlSerializerFormatters() //设置支持XML格式输入输出
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

      //禁用默认行为
      services.Configure<ApiBehaviorOptions>(options =>
      {
        options.SuppressModelStateInvalidFilter = true;
      });
    }

当我们禁用完之后,需要我们自定义的返回结果了,我们使用上面的定义的ApiValidationFilter进行拦截和返回。需要在ConfigureServices方法里面把这个拦截器注册进来

public void ConfigureServices(IServiceCollection services)
    {
      .....
      services
         .AddMvc(options =>
         {
           options.Filters.Add<ApiValidationFilter>();
         })
        .AddXmlSerializerFormatters() //设置支持XML格式输入输出
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    }

方案二  

这也是官网的推荐的做法是,若要自定义验证错误引发的响应,请使用InvalidModelStateResponseFactory。这个InvalidModelStateResponseFactory是一个参数为ActionContext,返回值为IActionResult的委托,具体实现如下:  

public void ConfigureServices(IServiceCollection services)
    {
      services
         .AddMvc()
        .AddXmlSerializerFormatters() //设置支持XML格式输入输出
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

      //参数验证
      services.Configure<ApiBehaviorOptions>(options =>
      {
        options.InvalidModelStateResponseFactory = (context) =>
        {
          var error = context.ModelState.GetValidationSummary();

          return new JsonResult(Result.FromError($"参数验证不通过:{error.ToString()}", ResultCode.InvalidParams));
        };
      });
    }

上面的代码是覆盖ModelState管理的默认行为(ApiBehaviorOptions),当数据模型验证失败时,程序会执行这段代码。没通过验证的ModelState,把它抛出的错误信息通过格式化利用JsonResult返回给客户端。

总结

我们在实际应用过程中,针对WebApi的开发基本上对于所有的请求都是要返回自定义结果的,所以我们需要覆盖默认的覆盖默认的模型认证行为,上面给出了两种方案:

第一种方案:符合Framework时代的风格,需要额外在指定覆盖原有的模型验证(SuppressModelStateInvalidFilter = true)

第二种方案:官方建议做法,符合Core时代的风格,只需复写InvalidModelStateResponseFactory委托即可,个人也推荐第二种方案。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • Asp.net core WebApi 使用Swagger生成帮助页实例

    最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core webapi来进行开发,开始每次调试以及与前端人员的沟通上都存在这效率低下的问题,一次在看微软asp.net core官方文档的时候,发现了swagger这个好东西.然后在实际的项目中引入了该技术.我们开发人员测试自己写的api的过程大大得到了简化,前端人员也可以根据我们提供的swagger help pages 自己进行一些前端代码的测试,大大提高了前后端的开发效

  • 详解ASP.NET Core WebApi 返回统一格式参数

    业务场景: 业务需求要求,需要对 WebApi 接口服务统一返回参数,也就是把实际的结果用一定的格式包裹起来,比如下面格式: { "response":{ "code":200, "msg":"Remote service error", "result":"" } } 具体实现: using Microsoft.AspNetCore.Mvc; using Microsoft.AspNe

  • asp.net core 2.0 webapi集成signalr(实例讲解)

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

  • 浅谈ASP.Net Core WebApi几种版本控制对比

    一.版本控制的好处: (1)有助于及时推出功能, 而不会破坏现有系统. (2)它还可以帮助为选定的客户提供额外的功能. API 版本控制可以采用不同的方式进行控制,方法如下: (1)在 URL 中追加版本或作为查询字符串参数, (2)通过自定义标头和通过接受标头 在这篇文章中, 让我们来看看如何支持多个版本的 ASP.NET Core Web API. 一.创建asp.net core webapi 项目,引用NuGet包:Install-Package Microsoft.AspNetCore

  • asp.net core webapi 服务端配置跨域的实例

    在前后端分离开发中服务端仅仅只为前端提供api接口,并且前后端往往单独部署,此时就会出现浏览器跨域问题.asp.net core提供了简单优雅的解决方案. 在startup文件的Configure添加如下代码(替换"http://localhost:8080"为你的前端部署地址,此处测试的前端地址为本地的8080端口) 注:asp.net core2.0以下需安装nuget包:Microsoft.AspNetCore.Cors app.UseCors(builder => { b

  • .Net Core2.1 WebAPI新增Swagger插件详解

    说明 Swagger是一个WebAPI在线注解.调试插件,过去我们主要通过手工撰写WebAPI接口的交互文档供前端开发人员或外部开发者, 官网地址:https://swagger.io/. 但是在实际工作中,往往咋们的文档工作通常落后于实际的环境,导致文档和实际接口不一致,前后端开发人员苦不堪言. Swagger的出现解放了接口文档撰写的麻烦也提高了前后端开发者的工作效率,所谓"工欲善其事,必先利其器 ".现在让咋们 了解下在.NET Core 2.1下如何实现Swagger. 1.N

  • ASP.NET Core WebApi中使用FluentValidation验证数据模型的方法

    介绍 验证用户输入是一个Web应用中的基本功能.对于生产系统,开发人员通常需要花费大量时间,编写大量的代码来完成这一功能.如果我们使用FluentValidation构建ASP.NET Core Web API,输入验证的任务将比以前容易的多. FluentValidation是一个非常流行的构建强类型验证规则的.NET库. 配置项目 第一步:下载FluentValidation 我们可以使用Nuget下载最新的 FluentValidation 库 PM> Install-Package Fl

  • Asp.Net Core WebAPI使用Swagger时API隐藏和分组详解

    1.前言 为什么我们要隐藏部分接口? 因为我们在用swagger代替接口的时候,难免有些接口会直观的暴露出来,比如我们结合Consul一起使用的时候,会将健康检查接口以及报警通知接口暴露出来,这些接口有时候会出于方便考虑,没有进行加密,这个时候我们就需要把接口隐藏起来,只有内部的开发者知道. 为什么要分组? 通常当我们写前后端分离的项目的时候,难免会遇到编写很多接口供前端页面进行调用,当接口达到几百个的时候就需要区分哪些是框架接口,哪些是业务接口,这时候给swaggerUI的接口分组是个不错的选

  • asp.net core webapi项目配置全局路由的方法示例

    一.前言 在开发项目的过程中,我新创建了一个controller,发现vs会给我们直接在controller头添加前缀,比如[Route("api/[controller]")],即在访问接口的时候会变成http://localhost:8000/api/values,但是如果控制器有很多个,或者要进行版本迭代时,我们会发现痛苦的时刻降临了,要一个一个的修改. 如果在这个时候可以进行全局配置前缀那真是福利呀,修改一处即可.为了能达到此目的我们就来运用一下吧. 二.配置 0.在配置前我们

  • ASP.NET Core实现自定义WebApi模型验证详解

    Framework时代 在Framework时代,我们一般进行参数验证的时候,以下代码是非常常见的 [HttpPost] public async Task<JsonResult> SaveNewCustomerAsnyc(AddCustomerInput input) { if (!ModelState.IsValid) { return Json(Result.FromCode(ResultCode.InvalidParams)); } ..... } 或者高级一点是实现IActionFi

  • ASP.NET Core 实现自定义WebApi模型验证实例讲解

    Framework时代 在Framework时代,我们一般进行参数验证的时候,以下代码是非常常见的 [HttpPost] public async Task<JsonResult> SaveNewCustomerAsnyc(AddCustomerInput input) { if (!ModelState.IsValid) { return Json(Result.FromCode(ResultCode.InvalidParams)); } ..... } 或者高级一点是实现IActionFi

  • ASP.NET Core如何自定义配置源示例详解

    前言 正如大家所知,在 .NET Core 中配置文件改成了 appsettings.json,表面上和 .NET Framework 的 web.config 或 app.config 好像没有太大的区别,只是一种是 json ,一种是 xml,但其实 .NET Core 的配置体系是一种全新的设计,灵活且具扩展性.这里主要介绍一下在 .NET Core 的配置体系下如何扩展自定义配置源,配置源其实就是配置信息存放的载体,最常用的就是文件类型. .NET Core 配置体系 在进行自定义配置源

  • ASP.NET Core 6.0 基于模型验证的数据验证功能

    目录 1 前言 1.1 数据验证的场景 1.2 本文的脉络 2 模型验证 2.1 介绍 2.2 基本使用 (1)自定义模型 (2)控制器代码 (3)测试 2.3 内置特性 3 自定义数据验证 3.1 介绍 3.2 前置准备 3.3 方案1:替换工厂 3.4 方案2:自定义过滤器 (1)自定义过滤器 (2)禁用默认过滤器 (3)启用自定义过滤器 3.5 测试 3.6 总结 4 源码解读 4.1 基本介绍 4.2 MvcServiceCollectionExtensions 4.3 ApiBehav

  • ASP.NET Core WebSocket集群实现思路详解

    目录 前言 实现 nginx配置 一对一发送 群组发送 发送所有人 整合到一起 一对一处理 群组处理 全员消息处理 示例源码 总结 前言 提到WebSocket相信大家都听说过,它的初衷是为了解决客户端浏览器与服务端进行双向通信,是在单个TCP连接上进行全双工通讯的协议.在没有WebSocket之前只能通过浏览器到服务端的请求应答模式比如轮询,来实现服务端的变更响应到客户端,现在服务端也可以主动发送数据到客户端浏览器.WebSocket协议和Http协议平行,都属于TCP/IP四层模型中的第四层

  • ASP.NET Core如何添加统一模型验证处理机制详解

    一.前言 模型验证自ASP.NET MVC便有提供,我们可以在Model(DTO)的属性上加上数据注解(Data Annotations)特性,在进入Action之前便会根据数据注解,来验证输入的数据是否合法,下面介绍以下如何统一处理验证并返回错误信息.话不多说了,来一起看看详细的介绍吧. 二.Action过滤器实现统一验证 我们在判断验证状态时一般会在Action里判断ModelState.IsValid是否为true. public IActionResult Create([FromBod

  • ASP.NET Core使用HostingStartup增强启动操作方法详解

    概念 在ASP.NET Core中我们可以使用一种机制来增强启动时的操作,它就是HostingStartup.如何叫"增强"操作,相信了解过AOP概念的同学应该都非常的熟悉.我们常说AOP使用了关注点分离的方式,增强了对现有逻辑的操作.而我们今天要说的HostingStartup就是为了"增强"启动操作,这种"增强"的操作甚至可以对现有的程序可以做到无改动的操作.例如,外部程序集可通过HostingStartup实现为应用提供配置服务.注册服务或

  • asp.net core中灵活的配置方式详解

    前言 asp.net core支持外部文件和命令行参数方式来配置系统运行所需要的配置信息,我们从下面两个常用场景来具体说下具体使用方法. 一.监听地址及端口配置 1,命令行方式 asp.net core系统通过命令行方式启动,使用的命令如下: dotnet run 上面的命令直接在源代码目录下执行,便可以编译程序并运行.那对于已经发布好的程序,就不能使用上面的指令了,应该使用下面的指令: dotnet 程序集文件名(程序集文件名就是程序发布后生成的dll文件) 上面两个指令都能够启动应用程序.程

  • ASP.NET Core MVC压缩样式、脚本详解

    前言 在.NET Core之前对于压缩样式文件和脚本我们可能需要借助第三方工具来进行压缩,但在ASP.NET MVC Core中则无需借助第三方工具来完成,本节我们来看看ASP.NET Core MVC为我们提供了哪些方便. 自动压缩样式和脚本 当我们在测试环境中肯定不需要压缩脚本的,如果一旦压缩脚本的话,若在控制台出现错误不利于我们调试,但是在生产环境中我们通过压缩脚本或者样式一来可以减少传输流量,二来可以加速页面加载时间,换句话说,此时我们需要测试环境和生产环境对应的原生版本和压缩版本,那么

  • Asp.Net Core中WebSocket绑定的方法详解

    说明 Websocket是html5后的产物,对于asp.net core中也得到了支持,Asp.Net Core中WebScoket的操作使用基本上和Asp.net中相同,不同的是,绑定监听. Asp.Net Core2.0默认已经支持WebSocket,不需要另外安装Nuget包. 通过对HttpContext中的WebSockets.AcceptWebSocketAsync方法,接受WebSocket请求:并返回WebScoket对象. 下面话不多说了,来一起看看详细的介绍吧. 一.示例1

随机推荐