ASP.NET MVC Ajax Fehlerbehandlung

117

Wie gehe ich mit Ausnahmen um, die in einem Controller ausgelöst werden, wenn jquery ajax eine Aktion aufruft?

Zum Beispiel möchte ich einen globalen Javascript-Code, der auf jeder Art von Serverausnahme während eines Ajax-Aufrufs ausgeführt wird und die Ausnahmemeldung im Debug-Modus oder nur eine normale Fehlermeldung anzeigt.

Auf der Client-Seite werde ich eine Funktion für den Ajax-Fehler aufrufen.

Muss ich auf der Serverseite einen benutzerdefinierten Aktionsfilter schreiben?

Shawn Mclean
quelle
8
Ein gutes Beispiel finden Sie in Beckelmans Post . Darins Antwort auf diesen Beitrag ist gut, aber setzen Sie nicht den richtigen Statuscode für einen Fehler.
Dan
6
Leider ist diese Verbindung jetzt unterbrochen
Chris Nevill
1
Hier ist der Link auf der Wayback-Maschine: web.archive.org/web/20111011105139/http://beckelman.net/post/…
BruceHill

Antworten:

161

Wenn der Server einen anderen Statuscode als 200 sendet, wird der Fehlerrückruf ausgeführt:

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

und um einen globalen Fehlerbehandler zu registrieren, können Sie die folgende $.ajaxSetup()Methode verwenden:

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

Eine andere Möglichkeit ist die Verwendung von JSON. Sie können also einen benutzerdefinierten Aktionsfilter auf den Server schreiben, der Ausnahmen abfängt und sie in eine JSON-Antwort umwandelt:

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

und dekorieren Sie dann Ihre Controller-Aktion mit diesem Attribut:

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

und schließlich aufrufen:

$.getJSON('/home/foo', { id: null }, function (result) {
    if (!result.success) {
        alert(result.error);
    } else {
        // handle the success
    }
});
Darin Dimitrov
quelle
1
Danke dafür. Letzteres war das, wonach ich gesucht habe. Gibt es für die asp.net mvc-Ausnahme eine bestimmte Möglichkeit, sie zu werfen, damit sie vom jquery-Fehlerbehandler abgefangen werden kann?
Shawn Mclean
1
@Lol-Codierer, unabhängig davon, wie Sie eine Ausnahme innerhalb der Controller-Aktion auslösen, gibt der Server den 500-Statuscode zurück und der errorRückruf wird ausgeführt.
Darin Dimitrov
Danke, perfekt, genau das, wonach ich gesucht habe.
Shawn Mclean
1
Wäre ein Statuscode von 500 nicht falsch? Um diesen Punkt zu zitieren : Broadcast.oreilly.com/2011/06/… : "Wenn Sie nicht erkennen, dass ein 4xx-Fehler bedeutet, dass ich es vermasselt habe, und ein 5xx-Fehler, dass Sie es vermasselt haben" - wo ich der Client und Sie der Server sind.
Chris Nevill
Diese Antwort gilt noch für die neueren Versionen von ASPNET?
Gog
73

Nach dem Googeln schreibe ich eine einfache Ausnahmebehandlung basierend auf dem MVC-Aktionsfilter:

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

und schreibe in global.ascx:

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

und schreiben Sie dieses Skript dann auf das Layout oder die Masterseite:

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

Schließlich sollten Sie den benutzerdefinierten Fehler aktivieren. und dann genieße es :)

Arash
quelle
Ich kann den Fehler in Firebug sehen, aber er leitet nicht zur Fehlerseite um.
user2067567
1
Danke dafür! sollte als Antwort IMO als seine Filterung auf Ajax-Anforderungen markiert werden und erbt die richtige Klasse und nicht das, was das HandleErrorAttribute erbt
mtbennett
2
Wunderbare Antwort! : D
Leniel Maccaferri
1
Ich denke, dass "Request.IsAjaxRequest ()" manchmal nicht so zuverlässig ist.
Will Huang
Für die Debug-Konfiguration funktioniert es immer, aber nicht immer in der Release-Konfiguration und gibt HTML zurück, stattdessen hat jemand eine Problemumgehung für einen solchen Fall?
Hitendra
9

Leider ist keine der Antworten gut für mich. Überraschenderweise ist die Lösung viel einfacher. Rückkehr vom Controller:

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

Und behandeln Sie es als Standard-HTTP-Fehler auf dem Client, wie Sie möchten.

Alehro
quelle
@ Will Huang: der Name der Ausnahmeinstanz
schmendrick
Ich muss das erste Argument vorbringen int. Wenn ich das mache, wird das Ergebnis auch an den ajax successHandler und nicht an den errorHandler übergeben. Ist das das erwartete Verhalten?
Jonathan Wood
4

Ich habe eine schnelle Lösung gefunden, weil ich wenig Zeit hatte und es gut funktionierte. Obwohl ich denke, dass die bessere Option die Verwendung eines Ausnahmefilters ist, kann meine Lösung möglicherweise helfen, falls eine einfache Lösung benötigt wird.

Ich habe folgendes getan. In der Controller-Methode habe ich ein JsonResult mit der Eigenschaft "Success" in den Daten zurückgegeben:

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

Später beim Ajax-Aufruf habe ich nur nach dieser Eigenschaft gefragt, um zu wissen, ob ich eine Ausnahme hatte:

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

Hoffe das hilft. Glücklicher Code! : P.

Daniel Silva
quelle
4

In Übereinstimmung mit der Antwort von aleho ist hier ein vollständiges Beispiel. Es funktioniert wie ein Zauber und ist super einfach.

Controller-Code

[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-Code in der Ansicht

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

Hoffe das hilft jemand anderem!

Rymnel
quelle
0

Für die Behandlung von Fehlern aus Ajax-Aufrufen auf der Clientseite weisen Sie der errorOption des Ajax-Aufrufs eine Funktion zu .

Um einen Standard global festzulegen, können Sie die hier beschriebene Funktion verwenden: http://api.jquery.com/jQuery.ajaxSetup .

Brian Ball
quelle
Eine Antwort, die ich vor über 4 Jahren gegeben habe, wird plötzlich abgelehnt? Möchte jemand einen Grund angeben, warum?
Brian Ball
1
Wenden Sie sich an SOF und fragen Sie den DBA, wer die Abwahl abgegeben hat. Als nächstes senden Sie eine Nachricht an diese Person, damit sie es erklären kann. Nicht jeder kann einen Grund dafür angeben.
JoshYates1980