繁体   English   中英

对单个 .NET 核心 API 操作禁用 model 验证

[英]Disable model validation for single .NET Core API action

我有一个 API controller 用于在我正在开发的应用程序上执行自动保存。 它使用与视图相同的视图模型,其中包含许多必填字段。 自动保存 controller 可能需要保存一个 model,如果用户在保存时未完成表单,则该字段被视为无效。 默认情况下,使用[ApiController]属性声明的 .NET Core controller 将自动强制验证。 我知道我可以在Startup.cs中这样禁用它:

services.Configure<ApiBehaviorOptions>(options =>
{
     options.SuppressModelStateInvalidFilter = true;
});

但这将适用于项目中的所有 API 控制器。 是否可以仅对一个controller 或操作禁用此默认验证? 到目前为止,我发现的所有内容都指示我使用上面的代码,但这并没有完成我正在寻找的东西。

您可以覆盖默认的InvalidModelStateResponseFactory

            services.Configure<ApiBehaviorOptions>(options =>
            {
                options.InvalidModelStateResponseFactory =
                    AllowingServerSideValidationToBeDisabledInvalidModelStateResponseFactoryHelper.InvalidModelStateResponseFactory;
            });

下面的InvalidModelStateResponseFactory检查 controller 操作上的OptionalValidationAttribute ,并搜索控制验证启用/禁用的表单/查询参数标志:

    // Code taken from https://github.com/dotnet/aspnetcore/blob/5747cb36f2040d12e75c4b5b3f49580ef7aac5fa/src/Mvc/Mvc.Core/src/DependencyInjection/ApiBehaviorOptionsSetup.cs#L23
    // and is modified to optionally disable validation for controller action methods decorated with OptionalValidationAttribute
    public static class AllowingServerSideValidationToBeDisabledInvalidModelStateResponseFactoryHelper
    {
        public static Func<ActionContext, IActionResult> InvalidModelStateResponseFactory => actionContext =>
        {
            var shouldEnableDataValidationarameterName = ((OptionalValidationAttribute)((ControllerActionDescriptor)actionContext.ActionDescriptor)
                .MethodInfo.GetCustomAttributes(typeof(OptionalValidationAttribute), true)
                .SingleOrDefault())?.ShouldEnableDataValidationParameterName;

            var isValidationEnabled = true;

            if (shouldEnableDataValidationarameterName != null)
            {
                var httpContextRequest = actionContext.HttpContext.Request;
                var shouldEnableDataValidationValue = httpContextRequest.Form[shouldEnableDataValidationarameterName]
                    .Union(httpContextRequest.Query[shouldEnableDataValidationarameterName]).FirstOrDefault();
                isValidationEnabled = shouldEnableDataValidationValue?.ToLower() == bool.TrueString.ToLower();
            }

            if (!isValidationEnabled)
            {
                return null;
            }

            var problemDetailsFactory = actionContext.HttpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>();
            var problemDetails = problemDetailsFactory.CreateValidationProblemDetails(actionContext.HttpContext, actionContext.ModelState);
            ObjectResult result;
            if (problemDetails.Status == 400)
            {
                // For compatibility with 2.x, continue producing BadRequestObjectResult instances if the status code is 400.
                result = new BadRequestObjectResult(problemDetails);
            }
            else
            {
                result = new ObjectResult(problemDetails)
                {
                    StatusCode = problemDetails.Status,
                };
            }
            result.ContentTypes.Add("application/problem+json");
            result.ContentTypes.Add("application/problem+xml");

            return result;
        };
    }

OptionalValidationAttribute

    [AttributeUsage(AttributeTargets.Method)]
    public class OptionalValidationAttribute : Attribute
    {
        public OptionalValidationAttribute(string shouldEnableDataValidationParameterName)
        {
            ShouldEnableDataValidationParameterName = shouldEnableDataValidationParameterName;
        }

        public string ShouldEnableDataValidationParameterName { get; }
    }

controller 操作的示例用法:

[HttpPost]
[OptionalValidation(shouldEnableDataValidationParameterName: "shouldEnableDataValidation")]
public async Task<IActionResult> Update(
    [FromForm] int id,
    [FromForm] string name,
    [FromForm] bool shouldEnableDataValidation
)
{
...
}

我建议您以不同的方式处理此问题:禁用 model 验证将意味着此操作没有任何验证; 不是现在,也不是以后。 仅仅因为您现在不需要验证并不意味着您以后不需要某种验证。

如果您使用一些自定义处理来完全禁用该操作的验证,那么您所做的就是在您的应用程序中创建一个实际的异常,这将使其更加复杂。 稍后查看此问题的开发人员可能不会预料到这种行为,并且可能会花费大量时间试图弄清楚为什么验证在它适用于所有其他操作时没有运行。

因此,请考虑仅复制 model 以便每个操作都有自己的 model:操作 A 具有带有验证属性的原始 model,需要填充值。 并且操作 B 具有该 model 的副本,没有任何验证属性。

虽然这看起来很浪费,但这确实给您带来了更多好处:

  • 如果您稍后需要对操作 B 上的某些字段进行验证,则只需添加一些验证属性即可。 您不必完全禁用自动验证,因此如果您将单个验证属性添加到 model,它们将继续工作。
  • 拥有单独的模型允许两个动作独立发展。 已经有一个很好的指标表明动作做了两件不同的事情:一个需要值,另一个不需要。 因此,这些模型在未来可能需要进一步分化并不是不可能的。 例如,您可能只想将属性添加到一个 model 而不是另一个。
  • 如上所述,您可以坚持默认行为并保持一致的开发体验。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM