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

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