Gestion des erreurs ASP.NET MVC Ajax

117

Comment gérer les exceptions lancées dans un contrôleur lorsque jquery ajax appelle une action?

Par exemple, je voudrais un code javascript global qui est exécuté sur n'importe quel type d'exception de serveur lors d'un appel ajax qui affiche le message d'exception en mode débogage ou juste un message d'erreur normal.

Du côté client, j'appellerai une fonction sur l'erreur ajax.

Du côté du serveur, dois-je écrire un filtre d'action personnalisé?

Shawn Mclean
la source
8
Voir le post de Beckelmans pour un bon exemple. La réponse de Darins à ce message est bonne, mais ne définissez pas le code d'état correct pour une erreur.
Dan
6
Malheureusement, ce lien est maintenant rompu
Chris Nevill
1
Voici le lien sur la machine de retour: web.archive.org/web/20111011105139/http://beckelman.net/post/…
BruceHill

Réponses:

161

Si le serveur envoie un code d'état différent de 200, le rappel d'erreur est exécuté:

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

et pour enregistrer un gestionnaire d'erreur global, vous pouvez utiliser la $.ajaxSetup()méthode:

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

Une autre façon consiste à utiliser JSON. Vous pouvez donc écrire un filtre d'action personnalisé sur le serveur qui intercepte les exceptions et les transforme en réponse 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
        };
    }
}

puis décorez l'action de votre contrôleur avec cet attribut:

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

et enfin l'invoquer:

$.getJSON('/home/foo', { id: null }, function (result) {
    if (!result.success) {
        alert(result.error);
    } else {
        // handle the success
    }
});
Darin Dimitrov
la source
1
Merci pour cela, ce dernier était ce que je recherchais. Donc, pour l'exception asp.net mvc, y a-t-il une manière spécifique dont j'ai besoin pour la lancer afin qu'elle puisse être interceptée par le gestionnaire d'erreurs jquery?
Shawn Mclean
1
@Lol codeur, quelle que soit la façon dont vous lancez une exception dans l'action du contrôleur, le serveur renverra le code d'état 500 et le errorrappel sera exécuté.
Darin Dimitrov le
Merci, parfait, exactement ce que je cherchais.
Shawn Mclean
1
Un code de statut de 500 ne serait-il pas un peu faux? Pour citer ce type broadcast.oreilly.com/2011/06/… : "Ne pas se rendre compte qu'une erreur 4xx signifie que j'ai foiré et qu'un 5xx signifie que vous avez foiré" - où je suis le client et vous est le serveur.
Chris Nevill
Cette réponse est toujours valable pour les nouvelles versions d'ASPNET?
gog
73

Après googler, j'écris un simple traitement d'exception basé sur 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);
        }
    }
}

et écrivez dans global.ascx:

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

puis écrivez ce script sur la mise en page ou la page maître:

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

Enfin, vous devez activer l'erreur personnalisée. et profitez-en :)

Arash
la source
Je peux voir l'erreur dans Firebug mais il ne redirige pas vers la page d'erreur.?
user2067567
1
Merci pour cela! devrait être marqué comme la réponse IMO comme son filtrage sur les demandes ajax et hérite de la classe correcte plutôt que de ce que le HandleErrorAttribute hérite
mtbennett
2
Merveilleuse réponse! : D
Leniel Maccaferri
1
Je pense que le "Request.IsAjaxRequest ()" n'est pas si fiable parfois.
Will Huang
Pour la configuration de débogage, cela fonctionne toujours mais ne fonctionne pas toujours dans la configuration de version et renvoie html à la place, quelqu'un a une solution de contournement pour un tel cas?
Hitendra le
9

Malheureusement, aucune des réponses n'est bonne pour moi. Étonnamment, la solution est beaucoup plus simple. Retour du contrôleur:

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

Et gérez-le comme une erreur HTTP standard sur le client comme vous le souhaitez.

Alehro
la source
@Will Huang: le nom de l'instance d'exception
schmendrick
Je dois lancer le premier argument sur int. De plus, lorsque je fais cela, le résultat est transmis au ajax successgestionnaire, et non au errorgestionnaire. Est-ce le comportement attendu?
Jonathan Wood
4

J'ai fait une solution rapide parce que je manquais de temps et cela a bien fonctionné. Bien que je pense que la meilleure option est d'utiliser un filtre d'exception, ma solution peut peut-être vous aider dans le cas où une solution simple est nécessaire.

J'ai fait ce qui suit. Dans la méthode du contrôleur, j'ai renvoyé un JsonResult avec une propriété "Success" dans les données:

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

Plus tard dans l'appel ajax, je viens de demander cette propriété pour savoir si j'avais une exception:

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

J'espère que cela t'aides. Bon code! : P

Daniel Silva
la source
4

En accord avec la réponse d'aleho, voici un exemple complet. Cela fonctionne comme un charme et est super simple.

Code contrôleur

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

Code Javascript dans la vue

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

J'espère que ceci aide quelqu'un d'autre!

Rymnel
la source
0

Pour gérer les erreurs des appels ajax côté client, vous attribuez une fonction à l' erroroption de l'appel ajax.

Pour définir globalement une valeur par défaut, vous pouvez utiliser la fonction décrite ici: http://api.jquery.com/jQuery.ajaxSetup .

Brian Ball
la source
Une réponse que j'ai donnée il y a plus de 4 ans obtient soudainement un vote défavorable? Quelqu'un veut-il donner une raison?
Brian Ball
1
Contactez SOF et demandez à leur DBA de demander qui a voté contre. Ensuite, envoyez un message à cette personne pour qu'elle puisse expliquer. Tout le monde ne peut pas expliquer pourquoi.
JoshYates1980