【问题标题】:Intercept bad requests before reaching controller in ASP.NET Core在 ASP.NET Core 中到达控制器之前拦截错误请求
【发布时间】:2018-08-16 06:09:32
【问题描述】:

如果收到的请求是 BadRequest,我有一个应用逻辑,为此我创建了一个过滤器:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            // Apply logic
        }
    }
}

在启动中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options => { options.Filters.Add<ValidateModelAttribute>(); });
}

控制器:

[Route("api/[controller]")]
[ApiController]
public class VerifyController : ControllerBase
{
    [Route("test")]
    [HttpPost]
    [ValidateModel]
    public ActionResult<Guid> validationTest(PersonalInfo personalInfo)
    {
        return null;
    }
}

型号:

public class PersonalInfo
{
    public string FirstName { get; set; }
    [RegularExpression("\\d{4}-?\\d{2}-?\\d{2}", ErrorMessage = "Date must be properly formatted according to ISO 8601")]
    public string BirthDate { get; set; }
}

问题是当我在行上放一个断点时:

if (!context.ModelState.IsValid)

仅当我发送的请求有效时,执行才会到达此行。为什么我发送一个错误的请求它没有通过过滤器?

【问题讨论】:

    标签: c# asp.net-core asp.net-core-mvc


    【解决方案1】:

    您已应用于控制器的[ApiController] 属性将Automatic HTTP 400 Responses 添加到MVC 管道,这意味着如果ModelState 无效,则不会执行您的自定义过滤器和操作。

    我看到了一些影响其工作方式的选项:

    1. 移除[ApiController]属性

      虽然您可以只删除[ApiController] 属性,但这也会导致它提供的一些其他功能的丢失,例如Binding source parameter inference

    2. 禁用自动 HTTP 400 响应

      这是来自docs 的示例,它显示了如何仅禁用此功能:

      services.AddControllers()
          .ConfigureApiBehaviorOptions(options =>
          {
              // ...
              options.SuppressModelStateInvalidFilter = true;
              // ...
          }
      

      此代码位于您的 StartupConfigureServices 方法中。

    3. 自定义生成的自动响应

      如果您只想向调用者提供自定义响应,则可以自定义返回的内容。我已经在另一个答案here 中描述了它是如何工作的。

    【讨论】:

      【解决方案2】:

      作为 .Net Core 生命周期中的属性过滤器,你处理不了。具有 ModelState 的过滤器层将在模型绑定之后运行。

      您可以使用 .Net Core 中间件处理它,如下https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1&tabs=aspnetcore2x

      【讨论】:

        【解决方案3】:

        如果您想在单个操作上抑制ModelStateInvalidFilter,请考虑使用https://docs.microsoft.com/en-us/answers/questions/297568/how-to-suppress-suppressmodelstateinvalidfilter-at.html 上建议的自定义属性。 (和类似的答案https://github.com/aspnet/Mvc/issues/8575

        public class SuppressModelStateInvalidFilterAttribute : Attribute, IActionModelConvention
             {
            
                 private const string FilterTypeName = "ModelStateInvalidFilterFactory";
            
                    
                 public void Apply(ActionModel action)
                 {
            
                     for (var i = 0; i < action.Filters.Count; i++)
                     {
                         //if (action.Filters[i] is ModelStateInvalidFilter)
                         if (action.Filters[i].GetType().Name == FilterTypeName)
                         {
                             action.Filters.RemoveAt(i);
                             break;
                         }
                     }
                }
             }
        

        使用示例

         [ApiController]
         public class PersonController
         {
             [SuppressModelStateInvalidFilter]
             public ActionResult<Person> Get() => new Person();
         }
        

        【讨论】:

          【解决方案4】:

          Log automatic 400 responses 中描述了一个用于记录的交集示例 在 Startup.ConfigureServices 中添加配置。

          services.AddControllers()
              .ConfigureApiBehaviorOptions(options =>
              {
                  // To preserve the default behavior, capture the original delegate to call later.
                  var builtInFactory = options.InvalidModelStateResponseFactory;
          
                  options.InvalidModelStateResponseFactory = context =>
                  {
                      var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Startup>>();
          
                      // Perform logging here.
          //E.g. logger.LogError($”{context.ModelState}”);
              logger.LogWarning(context.ModelState.ModelStateErrorsToString());
          
                      // Invoke the default behavior, which produces a ValidationProblemDetails response.
                      // To produce a custom response, return a different implementation of IActionResult instead.
                      return builtInFactory(context);
                  };
              });
          
          public static String ModelStateErrorsToString(this ModelStateDictionary modelState)
                  {
                      IEnumerable<ModelError> allErrors = modelState.Values.SelectMany(v => v.Errors);
                      StringBuilder sb = new StringBuilder();
                      foreach (ModelError error in allErrors)
                      {
                          sb.AppendLine($"error {error.ErrorMessage} {error.Exception}");
                      }
                      return sb.ToString();
                  }
          

          【讨论】:

            猜你喜欢
            • 2022-08-23
            • 2020-08-09
            • 1970-01-01
            • 2013-04-10
            • 2021-12-11
            • 1970-01-01
            • 1970-01-01
            • 2019-07-08
            • 2013-06-05
            相关资源
            最近更新 更多