如何在 ASP .NET MVC 中仅验证模型的一部分?

Posted

技术标签:

【中文标题】如何在 ASP .NET MVC 中仅验证模型的一部分?【英文标题】:How to validate only part of the model in ASP .NET MVC? 【发布时间】:2012-12-26 04:27:29 【问题描述】:

我有一个大型模型(大型我的意思是模型类包含很多字段/属性,并且每个都至少有一个验证属性(例如RequiredMaxLengthMinLength 等))。我不想创建一个包含大量输入的视图来让用户用数据填充模型,我想创建几个视图,用户将在其中填充部分模型字段并进入下一步(某种“wizard”)。在步骤之间重定向时,我将未填充的模型对象存储在 Session 中。如下所示:

型号:

public class ModelClass

    [MaxLength(100)] ...
    public string Prop1get;set;
    [MaxLength(100)] ...
    public string Prop2get;set;
    ...
    [Required][MaxLength(100)] ...
    public string Prop20get;set;

控制器:

[HttpPost]
public ActionResult Step1(ModelClass postedModel)
    
    // user posts only for example Prop1 and Prop2
    // so while submit I have completly emty model object
    // but with filled Prop1 and Prop2
    // I pass those two values to Session["model"]
    var originalModel = Session["model"] as ModelClass ?? new ModelClass();
    originalModel.Prop1 = postedModel.Prop1;
    originalModel.Prop2 = postedModel.Prop2;
    Session["model"] = originalModel;

    // and return next step view
    return View("Step2");


[HttpPost]
public ActionResult Step2(ModelClass postedModel)

    // Analogically the same
    // I have posted only Prop3 and Prop4

    var originalModel = Session["model"] as ModelClass;
    if (originalModel!=null)
    
        originalModel.Prop3 = postedModel.Prop3;
        originalModel.Prop4 = postedModel.Prop4;
        Session["model"] = originalModel;

        // return next step view
        return View("Step3");
    
    return View("SomeErrorViewIfSessionBrokesSomeHow")

Step1 视图只有Prop1Prop2 的输入,Step2 视图包含Prop3Prop4 等的输入。

但事情是这样的

当用户打开时,例如,第 1 步,并用长度超过 100 个字符的值填充 Prop1,客户端验证工作正常。但是,当然,我必须在控制器的服务器端验证这个值。如果我有完整的模型,我会执行以下操作:

if(!ModelState.IsValid) return View("the same view with the same model object");

所以用户必须再次填写表格并更正。 但是在第 1 步用户只填写了 20 个属性,我需要验证它们。我不能使用ModelState.IsValid,因为模型状态将无效。如您所见,Prop20 标记有[Required] 属性,当用户提交Prop1Prop2 时,Prop20 为空,这就是ModelState 无效的原因。当然,我可以允许用户进入第 2 步,填写所有步骤并仅在最后一步验证模型状态,但我不想让用户进入第 2 步,如果他填写的第 1 步不正确。我希望在控制器中进行此验证。 所以问题是: 如何仅验证模型的一部分?如何验证只有部分模型属性与其验证属性匹配?

【问题讨论】:

您可以为每个步骤创建不同的视图模型,这可能是一个单独的视图。像 ProductStep1ViewModel 和 ProductStep1View,但我会比这更好地命名它们。 @NickBray,ModelClass 是视图模型类,从原始模型映射,所以如果我创建 ModelClass 作为其他特定步骤类的组合,我将不得不为映射器添加大约 40 个映射规则来映射这些模型类,这将是一堆代码。我已经考虑过了,但感谢您的建议。 @DmytroTsiniavsky 您可以改用Value Injecter 之类的东西来避免必须明确设置映射规则。 【参考方案1】:

一种可能的解决方案:

    使用 ModelState.IsValidField(字符串键);

    if (ModelState.IsValidField("Name") && ModelState.IsValidField("Address"))
     ... 
    

然后在一切完成后使用:

if(ModelState.IsValid)  .. 

【讨论】:

如果你填写一个不存在的字符串,它总是会返回,请确保你输入正确。【参考方案2】:

我认为最优雅的方式是这样做:

List<string> PropertyNames = new List<string>()

    "Prop1",
    "Prop2"
;

if (PropertyNames.Any(p => !ModelState.IsValidField(p)))

    // Error

else

    // Everything is okay

或:

List<string> PropertyNames = new List<string>()

    "Prop1",
    "Prop2"
;

if (PropertyNames.All(p => ModelState.IsValidField(p)))

    // Everything is okay

else

    // Error

【讨论】:

【参考方案3】:

只是为了添加到现有的答案中。我不会硬编码属性名称,而是使用一个属性与其他验证属性一起添加,如下所示:

public class ValidationStageAttribute : Attribute

    public int StageNumber  get; private set; 
    public ValidationStageAttribute(int stageNumber)
    
        StageNumber = stageNumber;
    

现在我们可以在不了解模型本身的情况下获取属性名称,因此可以将部分验证提取到方法中(如果您经常使用它,您的基本控制器将是一个好地方)。

protected bool ValidateStage(object viewModel, int stageToValidate)

    var propertiesForStage = viewModel.GetType()
        .GetProperties()
        .Where(prop => prop.GetCustomAttributes(false).OfType<ValidationStageAttribute>().Any(attr => attr.StageNumber == stageToValidate))
        .Select(prop => prop.Name);

    return propertiesForStage.All(p => ModelState.IsValidField(p));

现在您在发布操作中需要做的就是致电ValidateStage(viewModel, 1)

【讨论】:

【参考方案4】:

在 MVC Core 中,这相当于:

if (ModelState.GetFieldValidationState("Name") == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid)

    // do something

但是,我建议在这种情况下简单地创建一个单独的视图模型

您的局部视图模型可以被更大的视图模型继承,因此您不必在代码中重复自己(DRY 原则)。

最好避免对属性名称进行硬编码!

【讨论】:

它适用于我的 Core 2.1 API 项目——因为 MVC 和 Web API 现在共享同一个 ControllerBase 类。您可以看到 ModelState 也存在于 Core 2 的 ControllerBase 中,docs.microsoft.com/en-us/dotnet/api/… 有很多在 .NET Core Web API 中使用 ModelState 验证的例子。 docs.microsoft.com/en-us/aspnet/web-api/overview/… 玩到你拿到为止 我已经提出了我自己的问题link .. 看看你和我是否在同一页上.. 我已经看到了你建议的链接..

以上是关于如何在 ASP .NET MVC 中仅验证模型的一部分?的主要内容,如果未能解决你的问题,请参考以下文章

asp.net mvc 3 在模型上运行验证

在 Asp.Net MVC 中禁用模型验证

如何在 ASP.NET MVC 模型验证中检查字符串是不是包含 HTML 代码?

如何仅在 ASP.NET MVC 中仅显示当前登录用户创建的项目

asp.net mvc 中模型验证如何处理数组呢?

如何更改 ASP.NET MVC 中 int 模型验证的 ErrorMessage?