我开始玩knockout.js并且这样做我使用了FromJsonAttribute(由Steve Sanderson创建)。我遇到了自定义属性未执行模型验证的问题。我把一个简单的例子放在一起 - 我知道它看起来像很多代码 - 但基本问题是如何强制在自定义模型绑定器中验证模型。
using System.ComponentModel.DataAnnotations;
namespace BindingExamples.Models
{
public class Widget
{
[Required]
public string Name { get; set; }
}
}
这是我的控制器:
using System;
using System.Web.Mvc;
using BindingExamples.Models;
namespace BindingExamples.Controllers
{
public class WidgetController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Widget w)
{
if(this.ModelState.IsValid)
{
TempData["message"] = String.Format("Thanks for inserting {0}", w.Name);
return RedirectToAction("Confirmation");
}
return View(w);
}
[HttpPost]
public ActionResult PostJson([koListEditor.FromJson] Widget w)
{
//the ModelState.IsValid even though the widget has an empty Name
if (this.ModelState.IsValid)
{
TempData["message"] = String.Format("Thanks for inserting {0}", w.Name);
return RedirectToAction("Confirmation");
}
return View(w);
}
public ActionResult Confirmation()
{
return View();
}
}
}
我的问题是模型在我的PostJson方法中始终有效。为了完整性,这里是FromJson属性的Sanderson代码:
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace koListEditor
{
public class FromJsonAttribute : CustomModelBinderAttribute
{
private readonly static JavaScriptSerializer serializer = new JavaScriptSerializer();
public override IModelBinder GetBinder()
{
return new JsonModelBinder();
}
private class JsonModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stringified = controllerContext.HttpContext.Request[bindingContext.ModelName];
if (string.IsNullOrEmpty(stringified))
return null;
var model = serializer.Deserialize(stringified, bindingContext.ModelType);
return model;
}
}
}
}
答案 0 :(得分:13)
FromJsonAttribute
只与模型绑定,就像你说的那样,没有验证。
您可以向FromJsonAttribute
添加验证,以便根据其DataAnnotations属性验证模型。
这可以使用TypeDescriptor
类来完成。
TypeDescriptor 提供有关组件特征的信息,例如其属性,属性和事件。
查看我的解决方案。我测试了它。
private class JsonModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stringified = controllerContext.HttpContext.Request[bindingContext.ModelName];
if (string.IsNullOrEmpty(stringified))
return null;
var model = serializer.Deserialize(stringified, bindingContext.ModelType);
// DataAnnotation Validation
var validationResult = from prop in TypeDescriptor.GetProperties(model).Cast<PropertyDescriptor>()
from attribute in prop.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(prop.GetValue(model))
select new { Propertie = prop.Name, ErrorMessage = attribute.FormatErrorMessage(string.Empty) };
// Add the ValidationResult's to the ModelState
foreach (var validationResultItem in validationResult)
bindingContext.ModelState.AddModelError(validationResultItem.Propertie, validationResultItem.ErrorMessage);
return model;
}
}
答案 1 :(得分:0)
谢谢,谢谢你,dknaack !!你的答案正是我想要的,除了我想在每个属性绑定后验证b / c我有依赖于其他属性的属性,如果依赖属性无效,我不想继续绑定
这是我新的BindProperty重载:
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor){
// if this is a simple property, bind it and return
if(_simplePropertyKeys.ContainsKey(propertyDescriptor.Name)){
this.BindSimpleProperty(bindingContext, propertyDescriptor);
// if this is complex property, only bind it if we don't have an error already
} else if (bindingContext.ModelState.IsValid){
this.BindComplexProperty(bindingContext, propertyDescriptor);
}
// add errors from the data annotations
propertyDescriptor.Attributes.OfType<ValidationAttribute>()
.Where(a => a.IsValid(propertyDescriptor.GetValue(bindingContext.Model)) == false)
.ForEach(r => bindingContext.ModelState.AddModelError(propertyDescriptor.Name, r.ErrorMessage));
}
答案 2 :(得分:0)
首先,我只是开始学习ASP.NET,所以不要认真对待我的解决方案。我发现this article和你一样,试图做一个自定义模型绑定器。没有验证。然后我刚用DefaultModelBinder和voula替换IModelBinder接口,它的工作原理。希望我能帮助别人