Как сделать форму 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.

Другие вопросы по тегам