问题:如何在发布+验证方案中更新ModelState.
我有一个简单的形式:
<%= Html.ValidationSummary() %> <% using(Html.BeginForm())%> <%{ %> <%=Html.TextBox("m.Value") %> <%} %>
当用户提交我想要验证输入时,在某些情况下我想为用户修复错误,让他知道他犯了一个已修复的错误:
[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.quick'n'dirty方法是(ab)使用DefaultModelBinder并覆盖通过简单地更改赋值方向将表单中的值分配给模型的方法;).使用DefaultModelBinder我不必解析id.以下代码(基于DefaultModelBinder的原始实现)是我的解决方案:
////// Updates ModelState using values from /// Source /// Prefix used by Binder. Argument name in Action (if not explicitly specified). 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/// (prefix)) { var prefixKey = bindingContext.ModelState.Keys.First (x => x == prefix); bindingContext.ModelState[prefixKey].Value = new ValueProviderResult(obj2, obj2.ToString(), bindingContext.ModelState[prefixKey].Value.Culture); } } } }
所以问题仍然存在:我做的事情是非常不寻常的还是我错过了什么?如果是前者,那么我怎样才能以更好的方式实现这些功能(使用现有的MVC基础设施)?
我知道这篇文章相当陈旧,但这是我之前遇到的一个问题,我只是想到了一个我喜欢的简单解决方案 - 只需在获得发布值后清除ModelState.
UpdateModel(viewModel); ModelState.Clear(); viewModel.SomeProperty = "a new value"; return View(viewModel);
并且视图必须使用(可能已修改的)视图模型对象而不是ModelState.
也许这是非常明显的.事后看来似乎如此!