Obsługa błędów ASP.NET MVC Ajax

117

Jak obsłużyć wyjątki zgłaszane w kontrolerze, gdy jquery ajax wywołuje akcję?

Na przykład chciałbym mieć globalny kod javascript, który jest wykonywany na jakimkolwiek wyjątku serwera podczas wywołania ajax, które wyświetla komunikat o wyjątku, jeśli jest w trybie debugowania lub po prostu zwykły komunikat o błędzie.

Po stronie klienta wywołam funkcję dotyczącą błędu Ajax.

Czy po stronie serwera muszę napisać niestandardowy filtr akcji?

Shawn Mclean
źródło
8
Dobry przykład można znaleźć w poście Beckelmansa . Odpowiedź Darinsa na ten post jest dobra, ale nie ustawiaj poprawnego kodu statusu dla błędu.
Dan,
6
Niestety ten link jest teraz uszkodzony
Chris Nevill,
1
Oto link na maszynie powrotnej
BruceHill

Odpowiedzi:

161

Jeśli serwer wyśle ​​kod stanu inny niż 200, zostanie wykonane wywołanie zwrotne błędu:

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

i aby zarejestrować globalną obsługę błędów, możesz użyć $.ajaxSetup()metody:

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

Innym sposobem jest użycie JSON. Możesz więc napisać niestandardowy filtr akcji na serwerze, który wyłapuje wyjątki i przekształca je w odpowiedź 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
        };
    }
}

a następnie udekoruj akcję kontrolera tym atrybutem:

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

i na koniec przywołaj to:

$.getJSON('/home/foo', { id: null }, function (result) {
    if (!result.success) {
        alert(result.error);
    } else {
        // handle the success
    }
});
Darin Dimitrov
źródło
1
Dzięki za to, to drugie było tym, czego szukałem. Czy w przypadku wyjątku mvc asp.net istnieje konkretny sposób, w jaki należy go zgłosić, aby mógł zostać przechwycony przez program obsługi błędów jquery?
Shawn Mclean,
1
@Lol koderze, bez względu na to, w jaki sposób wyrzucisz wyjątek wewnątrz akcji kontrolera, serwer zwróci kod statusu 500, a errorwywołanie zwrotne zostanie wykonane.
Darin Dimitrov
Dzięki, doskonale, właśnie tego szukałem.
Shawn Mclean,
1
Czy kod statusu 500 nie byłby zły? Cytując tego faceta broadcast.oreilly.com/2011/06/… : „Nie zdając sobie sprawy, że błąd 4xx oznacza, że ​​zawiodłem, a 5xx oznacza, że ​​zawiedliście” - gdzie ja jestem klientem, a ty jesteś serwerem.
Chris Nevill,
Ta odpowiedź jest nadal ważna w nowszych wersjach programu ASPNET?
gog
73

Po googlowaniu piszę prostą obsługę wyjątków w oparciu o filtr akcji 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);
        }
    }
}

i napisz w global.ascx:

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

a następnie napisz ten skrypt na układzie lub stronie wzorcowej:

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

Na koniec powinieneś włączyć niestandardowy błąd. a potem ciesz się tym :)

Arash
źródło
Widzę błąd w Firebug, ale nie przekierowuje do strony błędu.?
user2067567
1
Dzięki za to! powinien być oznaczony jako odpowiedź IMO, ponieważ filtruje żądania
Ajax
2
Wspaniała odpowiedź! : D
Leniel Maccaferri
1
Myślę, że czasami funkcja „Request.IsAjaxRequest ()” nie jest tak niezawodna.
Will Huang
W przypadku konfiguracji debugowania to zawsze działa, ale nie zawsze działa w konfiguracji wydania i zwraca html zamiast tego, czy ktoś ma obejście takiego przypadku?
Hitendra
9

Niestety żadna z odpowiedzi nie jest dla mnie dobra. Zaskakujące jest to, że rozwiązanie jest znacznie prostsze. Zwrot ze sterownika:

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

I obsłuż to jako standardowy błąd HTTP na kliencie, jak chcesz.

alehro
źródło
@Will Huang: nazwa instancji wyjątku
schmendrick
Muszę rzucić pierwszy argument int. Ponadto, kiedy to robię, wynik jest przekazywany do ajax successprocedury obsługi, a nie do errorprocedury obsługi. Czy to jest oczekiwane zachowanie?
Jonathan Wood
4

Zrobiłem szybkie rozwiązanie, ponieważ nie miałem czasu i zadziałało dobrze. Chociaż myślę, że lepszą opcją jest użycie filtru wyjątków, być może moje rozwiązanie może pomóc w przypadku, gdy potrzebne jest proste rozwiązanie.

Zrobiłem co następuje. W metodzie kontrolera zwróciłem JsonResult z właściwością „Success” wewnątrz 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 }, };
    }

Później w wywołaniu Ajax właśnie poprosiłem o tę właściwość, aby wiedzieć, czy mam wyjątek:

$.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.')
    }
});

Mam nadzieję że to pomoże. Szczęśliwy kod! : P

Daniel Silva
źródło
4

Zgodnie z odpowiedzią aleho, oto kompletny przykład. Działa jak urok i jest super prosty.

Kod kontrolera

[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);
        }
    }
}

Kod JavaScript w widoku

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.
    }
});

Mam nadzieję, że to pomoże komuś innemu!

Rymnel
źródło
0

Aby obsługiwać błędy wywołane wywołaniami AJAX po stronie klienta, przypisujesz funkcję do erroropcji wywołania Ajax.

Aby ustawić wartość domyślną globalnie, możesz użyć funkcji opisanej tutaj: http://api.jquery.com/jQuery.ajaxSetup .

Brian Ball
źródło
Odpowiedź, której udzieliłem ponad 4 lata temu, nagle dostaje negatywny głos? Czy ktoś ma ochotę podać powód?
Brian Ball
1
Skontaktuj się z SOF i poproś swojego administratora o sprawdzenie, kto oddał głos przeciw. Następnie wyślij wiadomość do tej osoby, aby mogła wyjaśnić. Nie każdy może podać powód.
JoshYates1980