Можно ли вернуть пользовательскую ошибку из JsonResult в метод jQuery ajax error?

Как передать пользовательскую информацию об ошибках из метода ASP.NET MVC3 JsonResult в error (или success или complete, если необходимо) функцию jQuery.ajax()? В идеале я хотел бы иметь возможность:

  • По-прежнему выдавать ошибку на сервере (это используется для ведения журнала)
  • Получить пользовательскую информацию об ошибке на клиенте

Вот базовая версия моего кода:

Метод контроллера JsonResult

public JsonResult DoStuff(string argString)
{
    string errorInfo = "";

    try
    {
        DoOtherStuff(argString);
    }
    catch(Exception e)
    {
        errorInfo = "Failed to call DoOtherStuff()";
        //Edit HTTP Response here to include 'errorInfo' ?
        throw e;
    }

    return Json(true);
}

JavaScript

$.ajax({
    type: "POST",
    url: "../MyController/DoStuff",
    data: {argString: "arg string"},
    dataType: "json",
    traditional: true,
    success: function(data, statusCode, xhr){
        if (data === true)
            //Success handling
        else
            //Error handling here? But error still needs to be thrown on server...
    },
    error: function(xhr, errorType, exception) {
        //Here 'exception' is 'Internal Server Error'
        //Haven't had luck editing the Response on the server to pass something here
    }
});

Вещи, которые я пробовал (это не сработало):

  • Возврат информации об ошибке из блока catch
    • Это работает, но исключение не может быть выбрано
  • Редактирование ответа HTTP в блоке catch
    • Затем проверили xhr в обработчике ошибок jQuery
    • xhr.getResponseHeader() и т.д. содержит страницу ошибок ASP.NET по умолчанию, но ни одна из моих сведений
    • Я думаю, что это возможно, но я просто сделал это неправильно?

Ответ 1

Вы можете написать собственный фильтр ошибок:

public class JsonExceptionFilterAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.HttpContext.Response.StatusCode = 500;
            filterContext.ExceptionHandled = true;
            filterContext.Result = new JsonResult
            {
                Data = new
                {
                    // obviously here you could include whatever information you want about the exception
                    // for example if you have some custom exceptions you could test
                    // the type of the actual exception and extract additional data
                    // For the sake of simplicity let suppose that we want to
                    // send only the exception message to the client
                    errorMessage = filterContext.Exception.Message
                },
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
    }
}

а затем зарегистрировать его как глобальный фильтр или применить только к определенным контроллерам/действиям, которые вы собираетесь использовать с AJAX.

И на клиенте:

$.ajax({
    type: "POST",
    url: "@Url.Action("DoStuff", "My")",
    data: { argString: "arg string" },
    dataType: "json",
    traditional: true,
    success: function(data) {
        //Success handling
    },
    error: function(xhr) {
        try {
            // a try/catch is recommended as the error handler
            // could occur in many events and there might not be
            // a JSON response from the server
            var json = $.parseJSON(xhr.responseText);
            alert(json.errorMessage);
        } catch(e) { 
            alert('something bad happened');
        }
    }
});

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

$(document).ajaxError(function (evt, xhr) {
    try {
        var json = $.parseJSON(xhr.responseText);
        alert(json.errorMessage);
    } catch (e) { 
        alert('something bad happened');
    }
});

а затем:

$.ajax({
    type: "POST",
    url: "@Url.Action("DoStuff", "My")",
    data: { argString: "arg string" },
    dataType: "json",
    traditional: true,
    success: function(data) {
        //Success handling
    }
});

Еще одна возможность - адаптировать глобальный обработчик исключений, который я представил, чтобы внутри ErrorController вы проверяли, был ли он запросом AJAX, и просто возвращают детали исключения как JSON.

Ответ 2

Указанный выше совет не будет работать на IIS для удаленных клиентов. Они получат стандартную страницу с ошибкой, например, 500.htm вместо ответа с сообщением. Вы должны использовать режим customError в web.config или добавить

<system.webServer>
        <httpErrors existingResponse="PassThrough" />
    </system.webServer>

или

"Вы также можете зайти в диспетчер IIS → Страницы ошибок, затем нажмите прямо на" Редактировать настройки параметров... "и установите параметр" Подробное ошибок ", то это будет ваше приложение, которое обрабатывает ошибку и а не IIS".

Ответ 3

вы можете вернуть JsonResult с ошибкой и отслеживать статус на стороне javascript, чтобы показать сообщение об ошибке:

 JsonResult jsonOutput = null;
        try
        {
           // do Stuff
        }
        catch
        {
            jsonOutput = Json(
                 new
                 {
                     reply = new
                     {
                         status = "Failed",
                         message = "Custom message "
                     }
                 });
        }
        return jsonOutput ;

Ответ 4

Мой проект MVC не возвращал сообщение об ошибке (обычное или другое). Я обнаружил, что это сработало для меня:

$.ajax({
        url: '/SomePath/Create',
        data: JSON.stringify(salesmain),
        type: 'POST',
        contentType: 'application/json;',
        dataType: 'json',
        success: function (result) {

            alert("start JSON");
            if (result.Success == "1") {
                window.location.href = "/SomePath/index";
            }
            else {
                alert(result.ex);
            }

            alert("end JSON");
        },
        error: function (xhr) {

            alert(xhr.responseText);

        }
        //error: AjaxFailed
    });

Отображение xhr.responseText привело к очень подробному сообщению с форматированием в формате HTML.