Пользовательская модель Binder не проверяет модель
Я начал играть с knockout.js и при этом использовал атрибут FromJsonAttribute (созданный Стивом Сандерсоном). Я столкнулся с проблемой, когда пользовательский атрибут не выполняет проверку модели. Я собрал простой пример - я знаю, что это выглядит как много кода - но основная проблема заключается в том, как заставить проверку модели в пользовательском связывателе модели.
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:
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;
}
}
}
}
3 ответа
Описание
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;
}
}
Дополнительная информация
Прежде всего, я только начинаю изучать ASP.NET, поэтому не принимайте моё решение всерьез. Я нашел эту статью и, как и вы, попытался сделать пользовательскую модель переплета. Там не было никакой проверки. Затем я просто заменил интерфейс IModelBinder на DefaultModelBinder и voula, все работает. Надеюсь, я мог бы помочь кому-то
Спасибо, спасибо, dknaack!! Ваш ответ был именно тем, что я искал, за исключением того, что я хочу проверить после привязки каждого свойства, потому что у меня есть свойства, которые зависят от других свойств, и я не хочу продолжать связывать, если зависимое свойство недопустимо.
Вот моя новая перегрузка 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));
}