Обработка ошибок ASP.NET MVC Ajax

117

Как обрабатывать исключения, возникающие в контроллере, когда jquery ajax вызывает действие?

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

На стороне клиента я вызову функцию при ошибке ajax.

На стороне сервера: нужно ли мне писать настраиваемый фильтр действий?

Шон Маклин
источник
8
См. Хороший пример в публикации Бекельмана . Ответ Дарина на этот пост хорош, но не устанавливает правильный код статуса для ошибки.
Дэн
6
К сожалению, эта ссылка теперь не работает
Крис Невилл
1
Вот ссылка на обратную машину: web.archive.org/web/20111011105139/http://beckelman.net/post/…
Брюс Хилл

Ответы:

161

Если сервер отправляет код состояния, отличный от 200, выполняется обратный вызов ошибки:

$.ajax({
    url: '/foo',
    success: function(result) {
        alert('yeap');
    },
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert('oops, something bad happened');
    }
});

а для регистрации глобального обработчика ошибок вы можете использовать $.ajaxSetup()метод:

$.ajaxSetup({
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert('oops, something bad happened');
    }
});

Другой способ - использовать JSON. Таким образом, вы можете написать настраиваемый фильтр действий на сервере, который перехватывает исключение и преобразует их в ответ JSON:

public class MyErrorHandlerAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        filterContext.ExceptionHandled = true;
        filterContext.Result = new JsonResult
        {
            Data = new { success = false, error = filterContext.Exception.ToString() },
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
        };
    }
}

а затем украсьте действие вашего контроллера этим атрибутом:

[MyErrorHandler]
public ActionResult Foo(string id)
{
    if (string.IsNullOrEmpty(id))
    {
        throw new Exception("oh no");
    }
    return Json(new { success = true });
}

и, наконец, вызовите его:

$.getJSON('/home/foo', { id: null }, function (result) {
    if (!result.success) {
        alert(result.error);
    } else {
        // handle the success
    }
});
Дарин Димитров
источник
1
Спасибо за это, последнее было тем, что я искал. Итак, для исключения asp.net mvc, есть ли конкретный способ, которым мне нужно его выбросить, чтобы его можно было поймать обработчиком ошибок jquery?
Шон Маклин
1
@Lol coder, независимо от того, как вы генерируете исключение внутри действия контроллера, сервер вернет код состояния 500, и errorобратный вызов будет выполнен.
Дарин Димитров
Спасибо, отлично, именно то, что я искал.
Шон Маклин
1
Разве код состояния 500 не будет неправильным? Процитирую эту главу broadcast.oreilly.com/2011/06/… : «Непонимание того, что ошибка 4xx означает, что я напортачил, а 5xx означает, что вы испортили» - где я - клиент, а вы - сервер.
Крис Невилл
Этот ответ все еще актуален для более новых версий ASPNET?
gog
73

После поиска в Google я пишу простую обработку исключений на основе фильтра действий MVC:

public class HandleExceptionAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
        {
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            filterContext.Result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new
                {
                    filterContext.Exception.Message,
                    filterContext.Exception.StackTrace
                }
            };
            filterContext.ExceptionHandled = true;
        }
        else
        {
            base.OnException(filterContext);
        }
    }
}

и напишите в global.ascx:

 public static void RegisterGlobalFilters(GlobalFilterCollection filters)
 {
      filters.Add(new HandleExceptionAttribute());
 }

а затем напишите этот сценарий на макете или главной странице:

<script type="text/javascript">
      $(document).ajaxError(function (e, jqxhr, settings, exception) {
                       e.stopPropagation();
                       if (jqxhr != null)
                           alert(jqxhr.responseText);
                     });
</script>

Наконец, вы должны включить настраиваемую ошибку. а потом наслаждайся :)

Араш
источник
Я вижу ошибку в Firebug, но она не перенаправляет на страницу ошибок.?
user2067567
1
Спасибо за это! должен быть помечен как ответ IMO как его фильтрация по запросам ajax и наследует правильный класс, а не то, что наследует
HandleErrorAttribute
2
Замечательный ответ! : D
Лениэль Маккаферри
1
Я думаю, что «Request.IsAjaxRequest ()» иногда не так надежен.
Уилл Хуанг
Для конфигурации отладки он всегда работает, но не всегда работает в конфигурации выпуска и возвращает html, вместо этого у кого-нибудь есть обходной путь для такого случая?
Hitendra
9

К сожалению, ни один из ответов мне не подходит. На удивление решение намного проще. Возврат из контроллера:

return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Response.ReasonPhrase);

И обрабатывайте это как стандартную ошибку HTTP на клиенте, как хотите.

alehro
источник
@Will Huang: имя экземпляра исключения
шмендрик
Я должен передать первый аргумент int. Кроме того, когда я это делаю, результат передается ajax successобработчику, а не errorобработчику. Это ожидаемое поведение?
Джонатан Вуд
4

Я принял быстрое решение, потому что у меня не было времени, и оно сработало. Хотя я думаю, что лучшим вариантом является использование фильтра исключений, возможно, мое решение может помочь в случае, если требуется простое решение.

Я сделал следующее. В методе контроллера я вернул JsonResult со свойством «Success» внутри Data:

    [HttpPut]
    public JsonResult UpdateEmployeeConfig(EmployeConfig employeToSave) 
    {
        if (!ModelState.IsValid)
        {
            return new JsonResult
            {
                Data = new { ErrorMessage = "Model is not valid", Success = false },
                ContentEncoding = System.Text.Encoding.UTF8,
                JsonRequestBehavior = JsonRequestBehavior.DenyGet
            };
        }
        try
        {
            MyDbContext db = new MyDbContext();

            db.Entry(employeToSave).State = EntityState.Modified;
            db.SaveChanges();

            DTO.EmployeConfig user = (DTO.EmployeConfig)Session["EmployeLoggin"];

            if (employeToSave.Id == user.Id)
            {
                user.Company = employeToSave.Company;
                user.Language = employeToSave.Language;
                user.Money = employeToSave.Money;
                user.CostCenter = employeToSave.CostCenter;

                Session["EmployeLoggin"] = user;
            }
        }
        catch (Exception ex) 
        {
            return new JsonResult
            {
                Data = new { ErrorMessage = ex.Message, Success = false },
                ContentEncoding = System.Text.Encoding.UTF8,
                JsonRequestBehavior = JsonRequestBehavior.DenyGet
            };
        }

        return new JsonResult() { Data = new { Success = true }, };
    }

Позже в вызове ajax я просто попросил это свойство знать, есть ли у меня исключение:

$.ajax({
    url: 'UpdateEmployeeConfig',
    type: 'PUT',
    data: JSON.stringify(EmployeConfig),
    contentType: "application/json;charset=utf-8",
    success: function (data) {
        if (data.Success) {
            //This is for the example. Please do something prettier for the user, :)
            alert('All was really ok');                                           
        }
        else {
            alert('Oups.. we had errors: ' + data.ErrorMessage);
        }
    },
    error: function (request, status, error) {
       alert('oh, errors here. The call to the server is not working.')
    }
});

Надеюсь это поможет. Счастливый код! :П

Даниэль Сильва
источник
4

В соответствии с ответом Алехо, вот полный пример. Он работает как шарм и очень прост.

Код контроллера

[HttpGet]
public async Task<ActionResult> ChildItems()
{
    var client = TranslationDataHttpClient.GetClient();
    HttpResponseMessage response = await client.GetAsync("childItems);

    if (response.IsSuccessStatusCode)
        {
            string content = response.Content.ReadAsStringAsync().Result;
            List<WorkflowItem> parameters = JsonConvert.DeserializeObject<List<WorkflowItem>>(content);
            return Json(content, JsonRequestBehavior.AllowGet);
        }
        else
        {
            return new HttpStatusCodeResult(response.StatusCode, response.ReasonPhrase);
        }
    }
}

Код Javascript в представлении

var url = '@Html.Raw(@Url.Action("ChildItems", "WorkflowItemModal")';

$.ajax({
    type: "GET",
    dataType: "json",
    url: url,
    contentType: "application/json; charset=utf-8",
    success: function (data) {
        // Do something with the returned data
    },
    error: function (xhr, status, error) {
        // Handle the error.
    }
});

Надеюсь, это поможет кому-то другому!

Rymnel
источник
0

Для обработки ошибок из вызовов ajax на стороне клиента вы назначаете функцию errorопции вызова ajax.

Чтобы установить глобальное значение по умолчанию, вы можете использовать описанную здесь функцию: http://api.jquery.com/jQuery.ajaxSetup .

Брайан Болл
источник
Ответ, который я дал более 4 лет назад, внезапно получает отрицательное голосование? Кто-нибудь хочет объяснить, почему?
Брайан Болл,
1
Свяжитесь с SOF и попросите администратора базы данных узнать, кто проголосовал против. Затем отправьте сообщение этому человеку, чтобы он смог объяснить. Не каждый может объяснить, почему.
JoshYates1980