如何仅为一个 Razor 页面设置 `ValidationVisitor.MaxValidationDepth` = 1?

How can I set `ValidationVisitor.MaxValidationDepth` = 1 only for one Razor Page?

如何在本地为一个 Razor 页面更改 ValidationVisitor.MaxValidationDepth 以防止对子对象进行验证?

我不想像文档中描述的那样用 MvcOptions.MaxValidationDepth 全局更改它:Maximum recursion

这是一个后续问题。请检查我的原始问题以在此处查看对象结构:

我们可以从IObjectModelValidator开始。默认实现是内部的 (DefaultObjectValidator) 并继承自 ObjectModelValidator。基础(和抽象)class 要求派生 class 只实现一个名为 GetValidationVisitor 的方法。这是您在运行之前修改 ValidationVisitorMaxValidationDepth 的一个扩展点。默认实现 DefaultObjectValidator 只是将 MaxValidationDepth 设置为从 MvcOptions 获得的值。所以它像你说的那样在全球范围内应用。

IObjectModelValidator 的自定义实现中(当然我们让它继承自 ObjectModelValidator),您可以改为从当前上下文中获取 MaxValidationDepth

在执行每个页面处理程序之前,您可以设置您选择的MaxValidationDepth。为了使其成为标准(像横切关注点一样使用),我们可以创建一个 IAsyncPageFilter 作为一个属性,它可以应用于任何你想要的页面模型 class。

实现代码如下:

//the custom IObjectModelValidator (which is much like what from the source code 
//of DefaultObjectValidator)
public class ContextBasedObjectModelValidator : ObjectModelValidator
{
    readonly MvcOptions _mvcOptions;
    public ContextBasedObjectModelValidator(IModelMetadataProvider modelMetadataProvider, 
                                            IList<IModelValidatorProvider> validatorProviders,
                                            MvcOptions mvcOptions) : base(modelMetadataProvider, validatorProviders)
    {
        _mvcOptions = mvcOptions;
    }

    public override ValidationVisitor GetValidationVisitor(ActionContext actionContext, 
        IModelValidatorProvider validatorProvider, 
        ValidatorCache validatorCache, 
        IModelMetadataProvider metadataProvider, 
        ValidationStateDictionary validationState)
    {            
        var visitor = new ValidationVisitor(
            actionContext,
            validatorProvider,
            validatorCache,
            metadataProvider,
            validationState)
        {
            MaxValidationDepth = actionContext.HttpContext.Features.Get<IContextBasedMaxValidationDepthFeature>()?.MaxValidationDepth ?? _mvcOptions.MaxValidationDepth,
            ValidateComplexTypesIfChildValidationFails = _mvcOptions.ValidateComplexTypesIfChildValidationFails,
        };

        return visitor;
    }
}

//we need a class for the custom request feature to hold your context-based MaxValidationDepth
public interface IContextBasedMaxValidationDepthFeature
{
    int MaxValidationDepth { get; }
}
public class ContextBasedMaxValidationDepthFeature : IContextBasedMaxValidationDepthFeature
{
    public ContextBasedMaxValidationDepthFeature(int maxValidationDepth)
    {
        MaxValidationDepth = maxValidationDepth;
    }
    public int MaxValidationDepth { get; }
}

//here the page filter to help set your context-based MaxValidationDepth
[AttributeUsage(AttributeTargets.Class)]
public class MaxValidationDepthAttribute : Attribute, IAsyncPageFilter
{
    public MaxValidationDepthAttribute(int maxValidationDepth)
    {
        MaxValidationDepth = maxValidationDepth;
    }
    public int MaxValidationDepth { get; }
    public Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
    {
        //set the max validation depth from the predefined value (via the attribute)
        context.HttpContext.Features
               .Set<IContextBasedMaxValidationDepthFeature>(new ContextBasedMaxValidationDepthFeature(MaxValidationDepth));
        return next();
    }

    public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
    {
        return Task.CompletedTask;
    }
}

最后我们需要在 Startup.ConfigureServices:

中注册您的自定义 IObjectModelValidator
services.Replace(new ServiceDescriptor(typeof(IObjectModelValidator), sp => {
            var options = sp.GetRequiredService<IOptions<MvcOptions>>().Value;
            var metadataProvider = sp.GetRequiredService<IModelMetadataProvider>();
            return new ContextBasedObjectModelValidator(metadataProvider, options.ModelValidatorProviders, options);
        }, ServiceLifetime.Singleton));

使用它:

//suppose you want it to be 1
//for this specific page
[MaxValidationDepth(1)]
public class YourPageModel : PageModel {
     //...
}