Как сделать форму POST для приложения MVC 3 и получить десериализованный класс?
У меня есть следующая форма:
<form id="MakeDocumentForm" name="MakeDocumentForm"
action="Document/GetWordDocument" method="post"
enctype="application/json">
<button type="submit" style="float:right;">Make Word Document</button>
<textarea id="hiddenJson"
name="hiddenJson"
data-bind="text: ko.toJSON(viewModel.selectedDocument)"
rows="5" cols="100"
style="visibility:hidden;" >
</textarea>
</form>
data-bind
атрибут knockoutjs - но это не важно, текстовая область правильно содержит JSON, который является сериализованным объектом.
[HttpPost]
public void GetWordDocument(DocumentModel hiddenJson)
{
//hiddenJson is not a correctly populated instance of my DocumentModel class
//any MVC experts know what I am doing wrong?
}
Теперь, как мне сделать форму POST для приложения MVC 3 и получить десериализованный класс?
1 ответ
Если вы публикуете его через AJAX с типом контента, установленным на JSON, то MVC 3 сможет правильно связать его в действии вашего контроллера.
$.ajax({
url: location.href,
type: "POST",
data: ko.toJSON(viewModel),
datatype: "json",
contentType: "application/json charset=utf-8",
success: function (data) { alert("success"); },
error: function (data) { alert("error"); }
});
Однако, если, как и в вашем примере, вы хотите сделать обычную запись формы, которая включает в себя JSON, тогда вам нужно проделать дополнительную работу, поскольку MVC3 не будет автоматически связывать ее с вашей моделью, так как тип контента будет application/x- WWW-форм-urlencoded.
У Стива Сандерсона есть более старый образец, который демонстрирует правильную привязку отправленных данных JSON в действии вашего контроллера: http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/
Суть в том, что он создает атрибут с именем "FromJson", который выглядит следующим образом:
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;
return serializer.Deserialize(stringified, bindingContext.ModelType);
}
}
}
Тогда действие выглядит так:
[HttpPost]
public ActionResult Index([FromJson] IEnumerable<GiftModel> gifts)
Кроме того, если вам не нравится использовать атрибут, вы можете зарегистрировать тип, чтобы всегда использовать определенную модель связующего.
Вы можете создать модель связующего, которая выглядит следующим образом:
public class JsonModelBinder: IModelBinder
{
private readonly static JavaScriptSerializer serializer = new JavaScriptSerializer();
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stringified = controllerContext.HttpContext.Request[bindingContext.ModelName];
if (string.IsNullOrEmpty(stringified))
return null;
return serializer.Deserialize(stringified, bindingContext.ModelType);
}
}
Затем зарегистрируйте его в global.asax.cs, например:
ModelBinders.Binders.Add(typeof(DocumentModel), new JsonModelBinder());
Теперь вам не нужно использовать атрибут, и ваша DocumentModel будет связана правильно. Это будет означать, что вы всегда будете отправлять DocumentModel через JSON.