多态绑定器:当自定义模型绑定包装在另一个类中时,验证不起作用

Zok*_*oka 2 c# model-binding model-validation asp.net-core

我对本文中的多态绑定示例进行了一些更改。

  1. 为各个类的和属性添加[Required]属性。CPUIndexScreenSizeLaptopSmartPhone
  2. 运行示例并创建任何类型的设备,无需填写 CPU 索引或屏幕尺寸。
  3. 它运行正确 - 模型已绑定并经过验证(向您显示错误,即“需要 CPU 索引/屏幕尺寸”。

到目前为止还可以。

现在添加新类:

public class DeviceWrapper
{
    public Device Device { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

并修改AddDevice.cshtml.cs文件:

...
[BindProperty]
public DeviceWrapper Device { get; set; } // model type changed here

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    switch (Device.Device) // added Device. prefix to respect new model structure
    {
        case Laptop laptop:
            Message = $"You added a Laptop with a CPU Index of {laptop.CPUIndex}.";
            break;

        case SmartPhone smartPhone:
            Message = $"You added a SmartPhone with a Screen Size of {smartPhone.ScreenSize}.";
            break;
    }

    return RedirectToPage("/Index");
}
Run Code Online (Sandbox Code Playgroud)

还修改页面AddDevice.cshtml以尊重新的模型结构。即在所有属性中Device.{prop}为每个添加前缀。例子:Device.name

<select id="Device_Kind" name="Device.Device.Kind" asp-items="Model.DeviceKinds" class="form-control"></select>
Run Code Online (Sandbox Code Playgroud)

现在运行应用程序。将断点放入AddDeviceModel.OnPost方法中。与第一个示例中的操作相同 - 创建设备而不填充 CPU 索引或屏幕大小。ModelState.IsValid检查now的值true。模型已绑定,但未经验证。我应该做什么来对这个包装模型应用验证。

在我的示例分支上尝试一下: https: //github.com/zoka-cz/AspNetCore.Docs/tree/master/aspnetcore/mvc/advanced/custom-model-binding/3.0sample/PolymorphicModelBinding

Zok*_*oka 5

我发现,还有更多的问题:

首先,这里的官方示例以及示例源代码中都包含错误:

// Setting the ValidationState ensures properties on derived types are correctly 
bindingContext.ValidationState[newBindingContext.Result] = new ValidationStateEntry
{ Metadata = modelMetadata };
Run Code Online (Sandbox Code Playgroud)

应该是

// Setting the ValidationState ensures properties on derived types are correctly 
bindingContext.ValidationState[newBindingContext.Result.Model] = new ValidationStateEntry
{ Metadata = modelMetadata };
Run Code Online (Sandbox Code Playgroud)

(注意索引器 - 它应该是模型本身。模型实例是关键,通过它搜索 ValidationState 字典)。

但是,它并没有解决原始问题,也没有验证包装的模型。还有另一个问题,我认为这是 ASP.NET Core 中的错误(已报告- 请参阅详细信息)。抽象Device类未经验证,因为它被跳过(出于什么原因?),因为DeviceWrapper它的子属性没有验证器,因为它不考虑真实类型(LaptopSmartPhone)的元数据,而只考虑元数据对于抽象类型Device,它实际上没有验证器。

这一发现让我找到了我认为更好的解决方案。我必须强制验证器验证设备。我必须说类型的属性Device有验证器。IValidatableObject这可以通过在抽象类上实现来实现Device

public abstract class Device : IValidatableObject
{
    public string Kind { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        yield return ValidationResult.Success; // just to force validator to validate according to the correct metadata
    }
}
Run Code Online (Sandbox Code Playgroud)

可能会发生您无法更改抽象类的情况,那么您可能会发现第二种方式更可行:

public class ForceValidationAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        return true;
    }
}
public class DeviceWrapper
{
    [ForceValidation] // this will force the validator to work according to the correct metadata
    public Device Device { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这两种方法都不好,我相信它应该在 ASP.NET Core 中修复,但作为解决方法,在我看来它是更干净的解决方案,因为生成的 ModelState 是正确的。