Какова наилучшая стратегия для обработки необработанных Исключений (ответы на ошибку 500) в действиях Asp.Net MVC для запросов Ajax?

Я смущен тем, как справиться с этой ситуацией.

Обычно, когда возникает необработанное исключение ASP.Net, сервер отправляет обратно какое-либо сообщение HTML, либо обработчик ошибок Asp.Net по умолчанию, либо специальный обработчик ошибок. В любом случае, HTML отправляется обратно (и обычно это хорошая идея сделать страницу удобной для пользователя).

Однако у меня возникла проблема, когда необработанные исключения происходят в действиях контроллера Asp.net MVC, которые, как ожидается, возвратят JSON для вызовов Ajax. Когда javascript читает возвращенную страницу (которая является HTML вместо предполагаемого JSON), она вылетает из-за невозможности конвертировать ответ в JSON (сейчас я использую ExtJS). Я хочу, чтобы Json был возвращен при исключении, чтобы пользователь мог быть уведомлен о том, что произошла ошибка.

Единственное решение, о котором я могу думать, - сделать следующее в каждом действии, которое возвращает Json:

try { .... }
catch (Exception ex)
{
   return Json(new { success = false, msg = ex.Message });
}

Мне не нравится этот метод, потому что он требует, чтобы я улавливал все исключения (что плохо по понятным причинам), и он требует, чтобы я перекачал каждое действие JsonResult с тем же кодом обработки исключений (что затрудняет его изменение позже).

Есть ли лучший способ вернуть более подходящий результат ошибки только для методов действий, которые возвращают Json, но все же сохраняют регулярные страницы ошибок, удобные для веб-запросов без Ajax?

Ответы

Ответ 1

Вот как я решил эту проблему:

public class HandleJsonError : HandleErrorAttribute
{
    public override void OnException(ExceptionContext exceptionContext)
    {
        if (!exceptionContext.HttpContext.Request.IsAjaxRequest() || exceptionContext.Exception == null) return;

        exceptionContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
        exceptionContext.Result = new JsonResult
                                   {
                                       Data = new
                                                  {
                                                      exceptionContext.Exception.Message,
                                                      exceptionContext.Exception.StackTrace
                                                  }
                                   };

        exceptionContext.ExceptionHandled = true;
    }
}

Ответ 2

Вы можете переопределить OnException в контроллере и вставить некоторую логику, чтобы вернуть ответ об ошибке JSON, который вам нужно будет обрабатывать в коде вызова javascript:

protected override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = Json("...");
        }
        else
        {
            base.OnException(filterContext);
        }
    }

Если вы хотите сделать это "правильно", вы можете написать actionfilter с почти таким же кодом и применить его ко всем контроллерам. Если вы используете MVC3, вы можете сделать его глобальным фильтром, чтобы он применим ко всем контроллерам.

Ответ 3

Вы можете использовать пользовательский ActionFilter, а затем внутри него, если это был запрос javascript, вы можете отформатировать ответ как объект JSON вместо HTML-страницы. HandleError было бы хорошим местом для изучения расширения базового класса.

Альтернативным вариантом, если вы не хотите делать фильтр actio, было бы переопределить OnException в вашем контроллере и установить exceptionContext.Result для нового JsonResult, который указывает на сбой.