Отправка JSON в контроллер

У меня есть это в моем контроллере:

[HttpPost]
public void UpdateLanguagePreference(string languageTag) {
 if (string.IsNullOrEmpty(languageTag)) {
  throw new ArgumentNullException("languageTag");
 }
 ...
}

И пусть этот код jQuery отправляется в контроллер:

jQuery.ajax({
  type: 'POST',
  url: '/Config/UpdateLanguagePreference',
  contentType: 'application/json; charset=utf-8',
  data: '{ "languageTag": "' + selectedLanguage + '" }'
});

Однако, когда я пытаюсь ввести код, я получаю сообщение об ошибке:

Server Error in '/' Application.
Value cannot be null.
Parameter name: languageTag

В чем проблема? Разве это не то, как отправить JSON на контроллер MVC? Я могу проверить POST с помощью Fiddler и убедиться, что запрос правильный. По какой-то причине UpdateLanguagePreference() получает нулевую или пустую строку.


person JamesBrownIsDead    schedule 18.05.2010    source источник


Ответы (4)


Очень важное предостережение, даже в MVC3, о том, как работает MVC3.

Если вы передаете объект, например, скажите:

​{
    Test: 'Hi'
}

И принимающий класс:

public class MyModel
{
    public string Test { get; set; }
}

С приемным методом контроллера, например:

[HttpPost]
public JsonResult Submit(MyModel model)
{
    . . .

Это будет работать. Но если в вашем методе Controller есть это очень незначительное, казалось бы, безобидное изменение:

[HttpPost]
public JsonResult Submit(MyModel test)
{
    . . .

Это не удастся. Это связано с тем, что среда MVC использует JSON в словаре, как указано выше, и видит, что один из параметров имеет то же имя, без учета регистра, что и один из его ключей ("Test"/"test"). Затем он принимает строковое значение «Привет», присвоенное Test, и передает его этому аргументу «test», даже если это явно не то, что имел в виду автор.

Что наиболее проблематично в этом, так это то, что фреймворк не выдает ошибку, пытаясь присвоить строку аргументу типа MyModel, что, по крайней мере, даст вам представление о том, что пошло не так. Он не видит, что это был неправильный тип и откат к его альтернативному поведению (которое он бы преследовал, если бы эти аргументы/свойства не совпадали по имени). Он просто молча терпит неудачу и присваивает null вашему аргументу, оставляя вас без понятия о том, что происходит.

Я неоднократно сталкивался с этой проблемой и, наконец, нашел сбой, из-за которого вещи, кажется, случайным образом перестают работать в MVC... Надеюсь, это поможет кому-то еще.

Поскольку любое разумное имя для этого аргумента действия является потенциально разумным именем свойства (модель, данные и т. д.) и поскольку оно нечувствительно к регистру, самый безопасный способ предотвратить его без написания собственного связывателя модели — стандартизировать одно, сумасшедшее, очень- имя аргумента, которое вряд ли когда-либо станет именем свойства, например:

[HttpPost]
public JsonResult Submit(MyModel _$_$twinkleTwinkleLittleFuckIt)
{

Но если у вас есть время, исправьте ModelBinder/JsonValueProviderFactory, чтобы свести риск к нулю вместо этой странной ошибки за десятилетие, до которой никто никогда не доберется.

person Chris Moschini    schedule 19.06.2012
comment
Это работает, как вы говорите, но если у меня есть список объектов внутри модели, он не привязывается. Он создает любой пустой список - person Bryida; 11.05.2017

Хм....

I do it

 $.post(target,
         {
             "ProblemId": id,
             "Status": update
         }, ProcessPostResult);

с участием

public class ProblemReportUpdate
    {
        public int ProblemId { get; set; }
        public string Status { get; set; }
    }

и

 [HttpPost]
 public ActionResult UpdateProblemReport(ProblemReportUpdate update)

цель установлена

var target = '<%=Url.Action("UpdateProblemReport", "ProblemReport") %>
person Keith Nicholas    schedule 18.05.2010

Вы публикуете строку, а не объект JSONified.

данные: '{ "languageTag": "' + selectedLanguage + '" }'

должно быть

данные: { "languageTag": selectedLanguage}

И убедитесь, что selectedLanguage определен в рамках вашего вызова ajax.

person Bora    schedule 18.05.2010
comment
Хм, на самом деле это не так. Объект JSONified представляет собой строку. Нет никакой разницы между строкой, которую я публикую, и JSONified-объектом с членом с именем languageTag. - person JamesBrownIsDead; 19.05.2010
comment
@JamesBrownIsDead -- На самом деле, насколько я понимаю, разница есть. Взгляните на мой ответ ниже. - person kdawg; 08.11.2010

Когда вы даете функции jQuery $.ajax() переменную данных в виде объекта javascript, она фактически переводит ее в серию пар ключ/значение и отправляет ее на сервер таким образом. Когда вы отправляете строковый объект json, он не в той форме, и стандартное связывание модели в mvc/mvc2 не обрезает его.

Если вы хотите отправлять данные сообщения таким образом, то вы почти у цели. Вам нужен класс JsonValueProviderFactory из бета-версии MVC2 Futures/MVC3. Взгляните на следующие ссылки для получения дополнительной информации:

http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx

... есть еще одна ссылка, которую я использовал при реализации этого самостоятельно, в которой больше говорилось о переносе класса JsonValueProviderFactory в ваше приложение, но я не могу найти ее для своей жизни.

person kdawg    schedule 08.11.2010