繁体   English   中英

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

[英]How to validate only part of the model in ASP .NET MVC?

我有一个大型模型(大型我的意思是模型类包含很多字段/属性,每个都至少有一个验证属性(如RequiredMaxLengthMinLength等))。 我没有创建一个带有大量输入的视图供用户使用数据填充模型,而是创建了几个视图,用户将填充部分模型字段并转到下一步(某种“向导” )。 在步骤之间重定向时,我在Session存储未填充的模型对象。 如下所示:

模型:

public class ModelClass
{
    [MaxLength(100)] ...
    public string Prop1{get;set;}
    [MaxLength(100)] ...
    public string Prop2{get;set;}
    ...
    [Required][MaxLength(100)] ...
    public string Prop20{get;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]属性,当用户提交Prop1Prop2Prop20为null,这就是ModelState无效的原因。 当然我可以允许用户转到步骤2,填写所有步骤并仅在最后一步验证模型状态,但我不想让用户在第1步填写错误时转到第2步。 我希望在控制器中进行此验证。 所以问题是: 我如何只验证模型的一部分? 如何验证只有部分模型属性与其验证属性匹配?

一种可能的方案:

  1. 使用ModelState.IsValidField(string key);

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

然后在一切结束时使用:

if(ModelState.IsValid) { .. }

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

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
}

只是为此添加现有答案。 我不会硬编码属性名称,而是使用一个属性与其他验证属性一起添加:

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)

在MVC Core中,这将相当于:

if (ModelState.GetFieldValidationState("Name") == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid)
{
    // do something
}

但是,我建议只在这个实例中创建一个单独的视图模型

部分视图模型可以由较大的视图模型继承,因此您不必在代码中重复自己(DRY主体)。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM