[英]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 的副本,没有任何验证属性。
虽然这看起来很浪费,但这确实给您带来了更多好处:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.