欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-28 20:42:44
framework时代   在framework时代,我们一般进行参数验证的时候,以下代码是非常常见的 [httppost] public async...

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委托即可,个人也推荐第二种方案。

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