Dmy*_*tro 19 c# model-view-controller asp.net-mvc model
我有一个大的模型(大我的意思是模型类中含有大量的字段/属性和每个具有至少一个验证属性(如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;}
}
Run Code Online (Sandbox Code Playgroud)
控制器:
[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")
}
Run Code Online (Sandbox Code Playgroud)
Step1视图具有输入仅Prop1与Prop2,第二步视图包含输入Prop3和Prop4等
但这就是事
当用户打开时,例如,步骤1,并使用超过100个字符长度的值填充Prop1,客户端验证工作正常.但是,当然,我必须在控制器中验证此值并在服务器端验证.如果我有完整模型,我只需执行以下操作:
if(!ModelState.IsValid) return View("the same view with the same model object");
Run Code Online (Sandbox Code Playgroud)
所以用户必须再次填写表格并更正.
但是在步骤1中,用户只填写了20个属性,我需要验证它们.我无法使用,ModelState.IsValid因为模型状态将无效.正如你可以看到Prop20标有[Required]属性,当用户提交Prop1和Prop2,Prop20为空,这就是为什么ModelState是无效的.当然我可以允许用户转到步骤2,填写所有步骤并仅在最后一步验证模型状态,但我不想让用户在第1步填写错误时转到第2步.我希望在控制器中进行此验证.所以问题是:
我如何只验证模型的一部分?如何验证只有部分模型属性与其验证属性匹配?
Nic*_*ray 15
一种可能的方案:
使用ModelState.IsValidField(string key);
if (ModelState.IsValidField("Name") && ModelState.IsValidField("Address"))
{ ... }
Run Code Online (Sandbox Code Playgroud)然后在一切结束时使用:
if(ModelState.IsValid) { .. }
Run Code Online (Sandbox Code Playgroud)
boi*_*iil 10
我认为最优雅的方式是这样做:
List<string> PropertyNames = new List<string>()
{
"Prop1",
"Prop2"
};
if (PropertyNames.Any(p => !ModelState.IsValidField(p)))
{
// Error
}
else
{
// Everything is okay
}
Run Code Online (Sandbox Code Playgroud)
要么:
List<string> PropertyNames = new List<string>()
{
"Prop1",
"Prop2"
};
if (PropertyNames.All(p => ModelState.IsValidField(p)))
{
// Everything is okay
}
else
{
// Error
}
Run Code Online (Sandbox Code Playgroud)
在 MVC Core 中,这相当于:
if (ModelState.GetFieldValidationState("Name") == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid)
{
// do something
}
Run Code Online (Sandbox Code Playgroud)
但是,我建议在这种情况下简单地创建一个单独的视图模型。
您的局部视图模型可以由较大的视图模型继承,因此您不必在代码中重复自己(DRY 主体)。
最好避免对属性名称进行硬编码!
只是为了增加现有的答案。与其对属性名称进行硬编码,不如使用以下属性将属性与其他验证属性一起添加:
public class ValidationStageAttribute : Attribute
{
public int StageNumber { get; private set; }
public ValidationStageAttribute(int stageNumber)
{
StageNumber = stageNumber;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以在不了解模型本身的情况下获取属性名称,可以将部分验证放入一个方法中(如果您经常使用它,那么基本控制器将是一个不错的选择)。
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));
}
Run Code Online (Sandbox Code Playgroud)
现在,您需要采取的后续行动就是致电 ValidateStage(viewModel, 1)
| 归档时间: |
|
| 查看次数: |
17215 次 |
| 最近记录: |