Сообщение jQuery JSON не удается при возврате null из ASP.NET MVC

Я использую ASP.NET MVC, чтобы опубликовать JSON из jQuery и получить JSON обратно, используя эту небольшую библиотечную функцию:

(function($) {
    $.postJson = function(url, data) {
        return $.ajax({
            url: url,

            data: JSON.stringify(data),
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json; charset=utf-8'
        });
    };
})(jQuery);

Итак, я назову это так:

$('#button').click(function() {
    $.postJson('/controller/action', { Prop1: 'hi', Prop2: 'bye' })
    .done(function(r) { alert('It worked.'); })
    .fail(function(x) { alert('Fail! ' + x.status); });
});

ASP.NET MVC 3 и ASP.NET MVC 4 поддерживают отправную точку (до этого вам нужно было расширить ASP.NET MVC для обработки отправки JSON), но проблема, с которой я сталкиваюсь, возвращается. На контроллере я часто возвращаю null, чтобы в основном сказать "Успех, больше ничего не могу сказать", например:

[HttpPost]
public JsonResult DoSomething(string Prop1, string Prop2)
{
    if (doSomething(Prop1, Prop2)
        return Json(null); // Success

    return Json(new { Message = "It didn't work for the following reasons" });
}

Я часто использую этот шаблон, и он отлично работает - мой успешный/сделанный обратный вызов вызван, и все хорошо. Но в последнее время я обновил ASP.NET MVC и jQuery, и он перестал работать - вместо этого мой отказ callback вызывается каждый раз, когда я return Json(null);. Кроме того, я проверил ответ, и возвращаемый статусCode на самом деле составляет 200, поэтому сервер не терпит неудачу - jQuery просто говорит, что это так.

Ответ 1

Проблема была вызвана обновлением с jQuery 1.8 до 1.9. В jQuery 1.7 и 1.8 это в MVC:

return Json(null);

был принят как действительный JSON и интерпретирован как null. Технически это отправляет пустую строку обратно клиенту с HTTP 200, и это достаточно хорошо для jQuery < 1.9.

Но теперь (мы используем jQuery 1.9.1), он пытается разобрать пустую строку как JSON, JSON-парсер jQuery генерирует исключение в пустой строке и запускает цепочку кодов, которая заканчивается обратным вызовом fail() вместо этого.

Обходной путь заключается в том, чтобы вместо этого вернуть сервер с успехом без дополнительной информации:

return Json(new{});

Это передается с парсером JQuery JSON, и все хорошо. Это также работает:

return Json(true);


Update

Заметки Мусы ниже этого поведения MVC кажутся нарушенными. Этот отдельный ответ на использование JSON.NET в качестве сериализатора JSON по умолчанию в ASP.NET MVC 3 - возможно ли это? описывает, как получить MVC для возврата null для Json(null) - в основном, используйте Json.NET вместо ASP.NET MVC встроенного JSON-сериализатора. Это решение, которое я в конечном итоге использовал.

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


Обновление 2

Стандартная реализация ISO 8601 Dates в Json.NET прерывает Internet Explorer 9 и ниже, когда он пытается проанализировать ее с помощью new Date(...). Другими словами, эти синтаксические разборки в Internet Explorer 9:

var date = new Date('2014-09-18T17:21:57.669');
var date = new Date('2014-09-18T17:21:57.600');

Но это вызывает исключение:

var date = new Date('2014-09-18T17:21:57.6');

Internet Explorer 9. Функция Date() не может справиться ни с чем, а ровно три миллисекунды. Чтобы исправить это, вы должны переопределить формат даты Json.NET, чтобы заставить его. Включено в код ниже.

public class JsonNetResult : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var response = context.HttpContext.Response;

        response.ContentType = !String.IsNullOrEmpty(ContentType) ? ContentType : "application/json";

        if (ContentEncoding != null)
            response.ContentEncoding = ContentEncoding;

        var settings = new JsonSerializerSettings
        {
            Converters = new[] {new IsoDateTimeConverter
            {
                DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"
            }}
        };
        var jsonSerializer = JsonSerializer.Create(settings);

        jsonSerializer.Serialize(response.Output, Data);
    }
}

A Gist, демонстрирующий, как связать это с BaseController:

https://gist.github.com/b9chris/6991b341e89bb0a4e6d801d02dfd7730