Как сделать форму POST для приложения MVC 3 и получить десериализованный класс? - PullRequest
3 голосов
/ 22 июля 2011

У меня есть следующая форма:

<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 Ответ

6 голосов
/ 23 июля 2011

Если вы публикуете его через 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-form-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.

...