【问题标题】:ASP.NET Core 2.0 ModelState Filter before Validation?验证前的 ASP.NET Core 2.0 ModelState 过滤器?
【发布时间】:2018-02-11 15:04:43
【问题描述】:

我可以在 ASP.NET Core 2.0 中做这样的事情吗?

例如,我有这个 ViewModel:

public class TodoViewModel
{
    [Required(ErrorMessage = "Required !")] // <--- Required
    public int? Key { get; set; } // Is Nullable

    [Required(ErrorMessage = "Required !")] // <--- Required
    public int? Value { get; set; } Nullable

    [Required(ErrorMessage = "Required !")] // <--- Required
    public byte Type { get; set; }
}

以视图为例,我有这个:

@Html.BeginForm(FormMethod.Post)
{ 
    @if (Model.Type == 1)
    {
        @Html.TextBoxFor(x => x.Key)
        @Html.ValidationMessageFor(x => x.Key)
    }

    @if (Model.Type == 2)
    {
        @Html.TextBoxFor(x => x.Value)
        @Html.ValidationMessageFor(x => x.Key)
    }
}

在行动中我有这个:

    [HttpPost]
    public IActionResult Todo(TodoViewModel model)
    {
        // My problem
        // if model.type = 1 return ModelState has error validate for Property (Value) is Required
        // and if model.type = 2 return ModelState has error validate for Property (Key) is Required
        if (!ModelState.IsValid) // <-- Here is my problem
            return View(model);
    }

我不想使用@Html.HiddenFor()

覆盖其他属性的验证

我尝试使用 ActionFilterAttribute:

public class TodoActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        TodoViewModel model = (TodoViewModel) context.ActionArguments["model"];

        if (model.Type == 1)
            model.Value = 0;

        if (model.Type == 2)
            model.Key = 0;
    }
}

但不幸的是,Execute for (OnActionExecuting) 是在 ModelState 为 Validation 之后。

有具体的方法吗?

如果“Type”属性等于 1,我想排除 ModelState 验证“Key”属性,

如果“Type”属性等于 2,则排除 ModelState 验证“Value”属性

【问题讨论】:

标签: asp.net-core-mvc asp.net-core-2.0


【解决方案1】:

实现 IValidatableObject 接口。

using System.ComponentModel.DataAnnotations;
public class TodoViewModel : IValidatableObject
{
    public int? Key { get; set; } 

    public int? Value { get; set; } 

    [Required(ErrorMessage = "Required !")] 
    public byte Type { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Type == 1)
        {
            yield return
              new ValidationResult(errorMessage: "Property (Value) is Required",
                                   memberNames: new[] { "Value" });
        }
        if (Type == 2)
        {
            yield return
              new ValidationResult(errorMessage: "Property (Key) is Required",
                                   memberNames: new[] { "Key" });
        }

   }
}

【讨论】:

    【解决方案2】:

    这清楚地表明您实际上需要两个 ViewModel。要么这样,要么你只对 ViewModel 做一个粗略的验证,然后在你的业务逻辑中做一个更详细的验证。

    当然,您可以编写自己的验证属性(但不是通过ActionFilterAttribute,而是通过从ValidationAttribute (Custom Validation Attribute) 继承并将其放在视图模型上而不是[Required])),这将检查是否需要一个,但是这实际上只是掩盖/隐藏了您拥有两种不同类型模型的意图。

    验证属性不是用来强制执行业务规则的,它们应该有点简单,并在应用业务验证之前验证数据是否看起来不错(这可能需要额外的检查、服务或数据库往返) .

    但请记住,如果您使用自定义验证属性,则不会有任何客户端验证(通过浏览器中的 JavaScript)。您还需要实现 AttributeAdapterBase&lt;T&gt; RequiredAttribute Example

    【讨论】:

    • 您需要做的就是让ValidationAttribute 实现IClientValidatable 以获得客户端验证
    • @StephenMuecke:介意提供示例或参考吗? ASP.NET Core 2.0 MVC存储库中没有这样的接口
    • 添加评论后,我意识到它是core-mvc 并重新标记了问题。其核心是IClientModelValidator
    • this answer 中的一个示例虽然还没有找到涵盖核心条件客户端验证的好博客(例如 this one 用于 mvc)
    猜你喜欢
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    • 2018-06-23
    • 2019-04-24
    相关资源
    最近更新 更多