如何在asp.net MVC中验证模型的一部分
本文关键字:验证 模型 一部分 MVC asp net | 更新日期: 2023-09-27 17:52:16
我有一个大模型(大我的意思是模型类包含很多字段/属性,每个至少有一个验证属性(如Required
, MaxLength
, MinLength
等))。我想创建几个视图,用户将在其中填充部分模型字段,然后进入下一步(某种"向导"),而不是创建一个带有大量输入的视图。在步骤之间重定向时,我在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
视图只包含Prop1
和Prop2
的输入,Step2视图包含Prop3
和Prop4
等的输入。
但是事情是这样的
当用户打开时,例如,步骤1,并且用超过100个字符长度的值填充Prop1,客户端验证工作正常。当然,我必须在服务器端控制器中验证这个值。如果我有完整的模型,我只需做以下操作:
if(!ModelState.IsValid) return View("the same view with the same model object");
所以用户必须重新填写表格并更正。BUT在步骤1中,用户只填写了20的2个属性,我需要验证它们。我不能使用ModelState.IsValid
,因为模型状态将无效。可以看到,Prop20
被标记为[Required]
属性,当用户提交Prop1
和Prop2
时,Prop20
为空,这就是为什么ModelState
无效。当然,我可以允许用户进入步骤2,填写所有步骤,只在最后一步验证模型状态,但我不想允许用户在填写步骤1错误的情况下进入步骤2。我想在控制器中进行验证。问题是:我如何验证模型的一部分?我如何验证只有一些模型属性与它们的验证属性匹配?
一个可能的解决方案:
-
使用状态。
IsValidField (string键);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
}
在MVC Core中,这相当于:
if (ModelState.GetFieldValidationState("Name") == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid)
{
// do something
}
但是,我建议在这个实例中简单地创建一个单独的视图模型
你的部分视图模型可以被你的更大的视图模型继承,所以你不必在代码中重复你自己(DRY原则)。
最好避免硬编码属性名!
只是为了添加到现有的答案。而不是硬编码属性名称,我将使用一个属性添加到您的其余验证属性沿行:
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));
}
现在你所需要做的就是在你的post action中调用ValidateStage(viewModel, 1)