Gestione degli errori Ajax ASP.NET MVC


117

Come gestisco le eccezioni lanciate in un controller quando jquery ajax chiama un'azione?

Ad esempio, vorrei un codice javascript globale che viene eseguito su qualsiasi tipo di eccezione del server durante una chiamata ajax che visualizza il messaggio di eccezione se in modalità di debug o solo un normale messaggio di errore.

Sul lato client, chiamerò una funzione sull'errore ajax.

Sul lato server, devo scrivere un actionfilter personalizzato?


8
Vedi beckelmans post per un buon esempio. La risposta di Darins a questo post è buona ma non impostare il codice di stato corretto per un errore.
Dan

6
Purtroppo quel collegamento è ora interrotto
Chris Nevill

Risposte:


161

Se il server invia un codice di stato diverso da 200, viene eseguito il callback dell'errore:

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

e per registrare un gestore di errori globale è possibile utilizzare il $.ajaxSetup()metodo:

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

Un altro modo è usare JSON. Quindi potresti scrivere un filtro di azione personalizzato sul server che cattura le eccezioni e le trasforma in una risposta 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
        };
    }
}

e quindi decora l'azione del controller con questo attributo:

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

e infine invocarlo:

$.getJSON('/home/foo', { id: null }, function (result) {
    if (!result.success) {
        alert(result.error);
    } else {
        // handle the success
    }
});

1
Grazie per questo, quest'ultimo era quello che stavo cercando. Quindi, per l'eccezione asp.net mvc, esiste un modo specifico per lanciarlo in modo che possa essere catturato dal gestore degli errori jquery?
Shawn Mclean

1
@Lol coder, non importa come lanci un'eccezione all'interno dell'azione del controller, il server restituirà un codice di stato 500 e la errorrichiamata verrà eseguita.
Darin Dimitrov

Grazie, perfetto, proprio quello che stavo cercando.
Shawn Mclean

1
Un codice di stato 500 non sarebbe sbagliato? Per citare questo tipo broadcast.oreilly.com/2011/06/… : "Non riuscire a rendersi conto che un errore 4xx significa che ho sbagliato e un 5xx significa che hai sbagliato" - dove io sono il client e tu sei il server.
Chris Nevill

Questa risposta è ancora valida per le versioni più recenti di ASPNET?
gog

73

Dopo aver cercato su Google scrivo una semplice gestione delle eccezioni basata su MVC Action Filter:

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

e scrivi in ​​global.ascx:

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

e quindi scrivi questo script sul layout o sulla pagina master:

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

Infine dovresti attivare l'errore personalizzato. e poi divertiti :)


Posso vedere l'errore in Firebug ma non viene reindirizzato alla pagina di errore.?
user2067567

1
Grazie per questo! dovrebbe essere contrassegnato come risposta IMO in quanto il suo filtraggio su richieste ajax ed eredita la classe corretta piuttosto che ciò che eredita da
HandleErrorAttribute

2
Risposta meravigliosa! : D
Leniel Maccaferri

1
Penso che "Request.IsAjaxRequest ()" non sia così affidabile a volte.
Will Huang

Per la configurazione di debug funziona sempre ma non funziona sempre nella configurazione di rilascio e restituisce html invece qualcuno ha una soluzione alternativa per questo caso?
Hitendra,

9

Sfortunatamente, nessuna delle risposte va bene per me. Sorprendentemente la soluzione è molto più semplice. Ritorno dal controller:

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

E gestiscilo come errore HTTP standard sul client come preferisci.


@ Will Huang: il nome dell'istanza di eccezione
schmendrick

Devo lanciare il primo argomento a int. Inoltre, quando lo faccio, il risultato viene passato al ajax successgestore e non al errorgestore. È questo il comportamento previsto?
Jonathan Wood,

4

Ho fatto una soluzione rapida perché avevo poco tempo e ha funzionato bene. Anche se penso che l'opzione migliore sia utilizzare un filtro di eccezione, forse la mia soluzione può aiutare nel caso in cui sia necessaria una soluzione semplice.

Ho fatto quanto segue. Nel metodo controller ho restituito un JsonResult con una proprietà "Success" all'interno dei dati:

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

Più tardi nella chiamata ajax ho solo chiesto a questa proprietà di sapere se avessi un'eccezione:

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

Spero che questo ti aiuti. Buon codice! : P


4

In accordo con la risposta di aleho ecco un esempio completo. Funziona come un fascino ed è semplicissimo.

Codice controller

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

Codice Javascript nella vista

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

Spero che questo aiuti qualcun altro!


0

Per la gestione degli errori dalle chiamate ajax sul lato client, si assegna una funzione errorall'opzione della chiamata ajax.

Per impostare un valore predefinito a livello globale, è possibile utilizzare la funzione descritta qui: http://api.jquery.com/jQuery.ajaxSetup .


Una risposta che ho dato più di 4 anni fa ottiene improvvisamente un voto negativo? Qualcuno vuole dare un motivo?
Brian Ball

1
Contatta SOF e chiedi al loro amministratore di database di chiedere chi ha dato il voto negativo. Quindi, invia un messaggio a quella persona in modo che possa spiegare. Non solo chiunque può spiegare perché.
JoshYates1980,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.