问题:如何在发布验证场景时更新ModelState.
我有一个简单的形式:
- <%= Html.ValidationSummary() %>
- <% using(Html.BeginForm())%>
- <%{ %>
- <%=Html.TextBox("m.Value") %>
- <input type="submit" />
- <%} %>
当用户提交我想要验证输入,在某些情况下,我想修复用户的错误,让他知道他做了一个已经修复的错误:
- [AcceptVerbs(HttpVerbs.Post)]
- public ActionResult Index(M m)
- {
- if (m.Value != "a")
- {
- ModelState.AddModelError("m.Value","should be \"a\"");
- m.Value = "a";
- return View(m);
- }
- return View("About");
- }
那么问题是,MVC会简单地忽略传递给视图的模型,并且会重新渲染用户键入的内容,而不是我的值(“a”).
这是因为TextBox渲染器检查是否有ModelState,如果它不为null – 则使用ModelState的值.该值当然是在发布之前键入的一个用户.
由于我无法更改TextBox渲染器的行为,我发现唯一的解决方案是自己更新ModelState.快速的方式是(ab)使用DefaultModelBinder,并通过简单地更改分配方向来覆盖从表单到模型分配值的方法;).使用DefaultModelBinder我不必解析ids.
以下代码(基于DefaultModelBinder的原始实现)是我的解决方案:
- /// <summary>
- /// Updates ModelState using values from <paramref name="order"/>
- /// </summary>
- /// <param name="order">Source</param>
- /// <param name="prefix">Prefix used by Binder. Argument name in Action (if not explicitly specified).</param>
- protected void UpdateModelState(object model,string prefix)
- {
- new ReversedBinder().BindModel(this.ControllerContext,new ModelBindingContext()
- {
- Model = model,ModelName = prefix,ModelState = ModelState,ModelType = model.GetType(),ValueProvider = ValueProvider
- });
- }
- private class ReversedBinder : DefaultModelBinder
- {
- protected override void BindProperty(ControllerContext controllerContext,ModelBindingContext bindingContext,System.ComponentModel.PropertyDescriptor propertyDescriptor)
- {
- string prefix = CreateSubPropertyName(bindingContext.ModelName,propertyDescriptor.Name);
- object val = typeof(Controller)
- .Assembly.GetType("System.Web.Mvc.DictionaryHelpers")
- .GetMethod("DoesAnyKeyHavePrefix")
- .MakeGenericMethod(typeof(ValueProviderResult))
- .Invoke(null,new object[] { bindingContext.ValueProvider,prefix });
- bool res = (bool)val;
- if (res)
- {
- IModelBinder binder = new ReversedBinder();//this.Binders.GetBinder(propertyDescriptor.PropertyType);
- object obj2 = propertyDescriptor.GetValue(bindingContext.Model);
- ModelBindingContext context2 = new ModelBindingContext();
- context2.Model = obj2;
- context2.ModelName = prefix;
- context2.ModelState = bindingContext.ModelState;
- context2.ModelType = propertyDescriptor.PropertyType;
- context2.ValueProvider = bindingContext.ValueProvider;
- ModelBindingContext context = context2;
- object obj3 = binder.BindModel(controllerContext,context);
- if (bindingContext.ModelState.Keys.Contains<string>(prefix))
- {
- var prefixKey = bindingContext.ModelState.Keys.First<string>(x => x == prefix);
- bindingContext.ModelState[prefixKey].Value
- = new ValueProviderResult(obj2,obj2.ToString(),bindingContext.ModelState[prefixKey].Value.Culture);
- }
- }
- }
- }
所以问题依然存在:我在做一些非常不寻常的事情,还是我错过了什么?如果是前者,那么如何以更好的方式实现这些功能(使用现有的MVC基础架构)?