首页 > 解决方案 > 禁用单个 .NET Core API 操作的模型验证

问题描述

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

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

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

标签: c#asp.net-core-mvcasp.net-core-3.1

解决方案


您可以覆盖默认值InvalidModelStateResponseFactory

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

下面InvalidModelStateResponseFactory检查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; }
    }

控制器操作的示例用法:

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

推荐阅读