如何仅为一个 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
的方法。这是您在运行之前修改 ValidationVisitor
的 MaxValidationDepth
的一个扩展点。默认实现 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 {
//...
}
如何在本地为一个 Razor 页面更改 ValidationVisitor.MaxValidationDepth
以防止对子对象进行验证?
我不想像文档中描述的那样用 MvcOptions.MaxValidationDepth
全局更改它:Maximum recursion
这是一个后续问题。请检查我的原始问题以在此处查看对象结构:
我们可以从IObjectModelValidator
开始。默认实现是内部的 (DefaultObjectValidator
) 并继承自 ObjectModelValidator
。基础(和抽象)class 要求派生 class 只实现一个名为 GetValidationVisitor
的方法。这是您在运行之前修改 ValidationVisitor
的 MaxValidationDepth
的一个扩展点。默认实现 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 {
//...
}