111 votes

ASP.NET MVC Gestion personnalisée des erreurs Application_Error Global.asax ?

J'ai un code de base pour déterminer les erreurs dans mon application MVC. Actuellement, dans le projet, j'ai un contrôleur appelé Error avec des méthodes d'action HTTPError404() , HTTPError500() et General() . Ils acceptent tous un paramètre de type chaîne de caractères error . En utilisant ou en modifiant le code ci-dessous, quelle est la meilleure façon de transmettre les données au contrôleur d'erreur pour le traitement ? Je veux que cette solution soit aussi robuste que possible.

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Response.Clear();

    HttpException httpException = exception as HttpException;
    if (httpException != null)
    {
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Error");
        switch (httpException.GetHttpCode())
        {
            case 404:
                // page not found
                routeData.Values.Add("action", "HttpError404");
                break;
            case 500:
                // server error
                routeData.Values.Add("action", "HttpError500");
                break;
            default:
                routeData.Values.Add("action", "General");
                break;
        }
        routeData.Values.Add("error", exception);
        // clear error on server
        Server.ClearError();

        // at this point how to properly pass route data to error controller?
    }
}

111voto

andrecarlucci Points 2435

Au lieu de créer une nouvelle route pour cela, vous pourriez simplement rediriger vers votre contrôleur/action et transmettre les informations via la chaîne de requête. Par exemple :

protected void Application_Error(object sender, EventArgs e) {
  Exception exception = Server.GetLastError();
  Response.Clear();

  HttpException httpException = exception as HttpException;

  if (httpException != null) {
    string action;

    switch (httpException.GetHttpCode()) {
      case 404:
        // page not found
        action = "HttpError404";
        break;
      case 500:
        // server error
        action = "HttpError500";
        break;
      default:
        action = "General";
        break;
      }

      // clear error on server
      Server.ClearError();

      Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
    }

Ensuite, votre contrôleur recevra ce que vous voulez :

// GET: /Error/HttpError404
public ActionResult HttpError404(string message) {
   return View("SomeView", message);
}

Il y a des compromis à faire avec votre approche. Soyez très très prudent avec les boucles dans ce type de traitement des erreurs. Par ailleurs, étant donné que vous passez par le pipeline asp.net pour traiter une erreur 404, vous allez créer un objet de session pour toutes ces occurrences. Cela peut poser un problème (de performance) pour les systèmes très utilisés.

31voto

Pour répondre à la question initiale "comment transmettre correctement les données de routage au contrôleur d'erreur ?" :

IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

Ensuite, dans votre classe ErrorController, implémentez une fonction comme celle-ci :

[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Error(Exception exception)
{
    return View("Error", exception);
}

Cela pousse l'exception dans la vue. La page de la vue doit être déclarée comme suit :

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<System.Exception>" %>

Et le code pour afficher l'erreur :

<% if(Model != null) { %>  <p><b>Detailed error:</b><br />  <span class="error"><%= Helpers.General.GetErrorMessage((Exception)Model, false) %></span></p> <% } %>

Voici la fonction qui rassemble tous les messages d'exception de l'arbre des exceptions :

    public static string GetErrorMessage(Exception ex, bool includeStackTrace)
    {
        StringBuilder msg = new StringBuilder();
        BuildErrorMessage(ex, ref msg);
        if (includeStackTrace)
        {
            msg.Append("\n");
            msg.Append(ex.StackTrace);
        }
        return msg.ToString();
    }

    private static void BuildErrorMessage(Exception ex, ref StringBuilder msg)
    {
        if (ex != null)
        {
            msg.Append(ex.Message);
            msg.Append("\n");
            if (ex.InnerException != null)
            {
                BuildErrorMessage(ex.InnerException, ref msg);
            }
        }
    }

11voto

Jozef Krchňavý Points 165

J'ai trouvé une solution pour le problème d'ajax noté par Lion_cl.

global.asax :

protected void Application_Error()
    {           
        if (HttpContext.Current.Request.IsAjaxRequest())
        {
            HttpContext ctx = HttpContext.Current;
            ctx.Response.Clear();
            RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
            rc.RouteData.Values["action"] = "AjaxGlobalError";

            // TODO: distinguish between 404 and other errors if needed
            rc.RouteData.Values["newActionName"] = "WrongRequest";

            rc.RouteData.Values["controller"] = "ErrorPages";
            IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
            IController controller = factory.CreateController(rc, "ErrorPages");
            controller.Execute(rc);
            ctx.Server.ClearError();
        }
    }

ErrorPagesController

public ActionResult AjaxGlobalError(string newActionName)
    {
        return new AjaxRedirectResult(Url.Action(newActionName), this.ControllerContext);
    }

AjaxRedirectResult

public class AjaxRedirectResult : RedirectResult
{
    public AjaxRedirectResult(string url, ControllerContext controllerContext)
        : base(url)
    {
        ExecuteResult(controllerContext);
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            JavaScriptResult result = new JavaScriptResult()
            {
                Script = "try{history.pushState(null,null,window.location.href);}catch(err){}window.location.replace('" + UrlHelper.GenerateContentUrl(this.Url, context.HttpContext) + "');"
            };

            result.ExecuteResult(context);
        }
        else
        {
            base.ExecuteResult(context);
        }
    }
}

AjaxRequestExtension

public static class AjaxRequestExtension
{
    public static bool IsAjaxRequest(this HttpRequest request)
    {
        return (request.Headers["X-Requested-With"] != null && request.Headers["X-Requested-With"] == "XMLHttpRequest");
    }
}

9voto

Jack Hsu Points 81

Je me suis déjà débattu avec l'idée de centraliser une routine globale de gestion des erreurs dans une application MVC. J'ai un post sur les forums ASP.NET .

Il gère essentiellement toutes les erreurs de votre application dans le fichier global.asax sans avoir besoin d'un contrôleur d'erreurs, en décorant avec la fonction [HandlerError] ou en manipulant l'attribut customErrors dans le web.config.

7voto

Brian Points 13596

Une meilleure façon de gérer les erreurs dans MVC consiste peut-être à appliquer l'attribut HandleError à votre contrôleur ou action et à mettre à jour le fichier Shared/Error.aspx pour faire ce que vous voulez. L'objet Model de cette page comprend une propriété Exception ainsi que ControllerName et ActionName.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X