web-dev-qa-db-fra.com

ASP.NET MVC Ajax Traitement des erreurs

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

Par exemple, je voudrais un code javascript global qui soit exécuté sur tout type d’exception de serveur lors d’un appel ajax qui affiche le message d’exception en mode débogage ou tout simplement un message d’erreur normal.

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

Sur le serveur, est-ce que je dois écrire un filtre d'action personnalisé?

114
Shawn Mclean

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 méthode $.ajaxSetup() :

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

Une autre façon consiste à utiliser JSON. Vous pouvez donc écrire sur le serveur un filtre d'action personnalisé qui capture l'exception et la 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 votre action de 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
    }
});
160
Darin Dimitrov

Après avoir googlé, j'écris une simple remise d'exception basée sur le filtre d'action 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);
        }
    }
}

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 puis en profiter :)

72
Arash Karami

Malheureusement, aucune des réponses ne me convient. Étonnamment, la solution est beaucoup plus simple. Retour du contrôleur:

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

Et traitez-le comme une erreur HTTP standard sur le client comme vous le souhaitez.

9
alehro

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

Code du 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!

4
Rymnel

J'ai fait une solution rapide parce que j'avais peu de temps et que ça fonctionnait bien. Bien que je pense que la meilleure option consiste à utiliser un filtre d'exception, peut-être que ma solution peut être utile 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é de 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. Code heureux! : P

4
Daniel Silva

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

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

0
Brian Ball