繁体   English   中英

在 Asp.Net Mvc 中未针对 Url 绑定调用自定义验证属性,但适用于 FromBody 绑定

[英]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.

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