[英]ASP.NET Core MVC Mixed Route/FromBody Model Binding & Validation
[英]Custom validation attribute is not called in Asp.Net Mvc for Url binding, but works for FromBody binding
我在 Mvc 中观察到一个奇怪的行为,不确定它是错误还是功能。
我有一个客户验证属性MyAttribute
并使用它,例如:
public async Task<IActionResult> GetData([MyAttribute] string myparam)
令人惊讶的是,如果此参数不在 url 中,则永远不会调用属性( IsValid
函数),但如果参数在 Url 中但为空(例如myParam=
),则会调用该属性( IsValid 函数)。 如果我有一个必需的属性,它也会被调用:
public async Task<IActionResult> GetData([MyAttribute, Required] string myparam)
现在,如果我有这样的请求 class :
public class MyRequest
{
[MyAttribute]
public string Test {get;set;}
}
public async Task<IActionResult> GetData([FromBody] MyRequest myparam)
然后即使没有Required
参数,也会调用该属性。
这里的某个地方有错误还是打算这样?
首先,如果我们使用 Asp.net 核心源代码进行调试将非常有帮助。 在此处查看更多信息: debug-net-core-source-visual-studio-2019 。 源码中这个问题的关键点是:EnforceBindRequiredAndValidate。 这是源代码:
private void EnforceBindRequiredAndValidate(
ObjectModelValidator baseObjectValidator,
ActionContext actionContext,
ParameterDescriptor parameter,
ModelMetadata metadata,
ModelBindingContext modelBindingContext,
ModelBindingResult modelBindingResult)
{
RecalculateModelMetadata(parameter, modelBindingResult, ref metadata);
if (!modelBindingResult.IsModelSet && metadata.IsBindingRequired)
{
// Enforce BindingBehavior.Required (e.g., [BindRequired])
var modelName = modelBindingContext.FieldName;
var message = metadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(modelName);
actionContext.ModelState.TryAddModelError(modelName, message);
}
else if (modelBindingResult.IsModelSet)
{
// Enforce any other validation rules
baseObjectValidator.Validate(
actionContext,
modelBindingContext.ValidationState,
modelBindingContext.ModelName,
modelBindingResult.Model,
metadata);
}
else if (metadata.IsRequired)
{
// We need to special case the model name for cases where a 'fallback' to empty
// prefix occurred but binding wasn't successful. For these cases there will be no
// entry in validation state to match and determine the correct key.
//
// See https://github.com/aspnet/Mvc/issues/7503
//
// This is to avoid adding validation errors for an 'empty' prefix when a simple
// type fails to bind. The fix for #7503 uncovered this issue, and was likely the
// original problem being worked around that regressed #7503.
var modelName = modelBindingContext.ModelName;
if (string.IsNullOrEmpty(modelBindingContext.ModelName) &&
parameter.BindingInfo?.BinderModelName == null)
{
// If we get here then this is a fallback case. The model name wasn't explicitly set
// and we ended up with an empty prefix.
modelName = modelBindingContext.FieldName;
}
// Run validation, we expect this to validate [Required].
baseObjectValidator.Validate(
actionContext,
modelBindingContext.ValidationState,
modelName,
modelBindingResult.Model,
metadata);
}
}
如果我们添加 required 属性条件 'else if(metadata.IsRequired)' 将被命中,然后它会调用 Validate function。
以下是问题请求和普通请求之间的区别: modelBindingResult 的值非常重要。 如果结果包含成功,那么它将在 function EnforceBindRequiredAndValidate 中调用 validate
从源代码中我们可以发现这是设计行为。
var modelBindingContext = DefaultModelBindingContext.CreateBindingContext(
actionContext,
valueProvider,
metadata,
parameter.BindingInfo,
parameter.Name);
modelBindingResult 来自上述上下文。 你会发现我们需要提供相关的provider和参数然后modelBindingResult.IsModelSet会被设置为true,这时候会调用function validate。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.