web-dev-qa-db-fra.com

ASP.NET MVC4 CustomErrors DefaultRedirect ignoré

J'ai une application MVC 4, qui utilise un HandleErrorAttribute personnalisé pour gérer uniquement les exceptions personnalisées. Je voudrais intercepter les pages d'erreur 404 par défaut et autres pages d'erreur autres que 500 et les remplacer par quelque chose de plus attrayant. À cette fin, j'ai ajouté ce qui suit à mon fichier Web.config:

<system.web>
    <customErrors mode="On" defaultRedirect="~/Error/Index" />
...
</ system.web>

J'ai un contrôleur d'erreur avec une méthode d'index et la vue correspondante, mais j'obtiens toujours la page d'erreur 404 par défaut. J'ai également essayé de définir ma defaultRedirect dans un fichier HTML statique en vain. J'ai essayé d'ajouter un traitement d'erreur spécifique à l'intérieur du <customErrors> du 404, et j'ai même essayé de modifier les itinéraires par programmation, le tout sans résultat. Qu'est-ce que je rate? Pourquoi ASP ignore-t-il le traitement des erreurs par défaut?

Remarque: J'ai remarqué précédemment que je ne pouvais pas tester mon CustomHandleErrorAttribute localement, même avec <customErrors mode="On". Cela fonctionne quand je tape sur mon serveur à partir de ma boîte de dev mais ... je ne sais pas si c'est lié. Ce gars avait le même problème.

13
therealmitchconnors

Cela devrait fonctionner:

1. Web.Config

<customErrors mode="On"
   defaultRedirect="~/Views/Shared/Error.cshtml">

  <error statusCode="403"
    redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />

  <error statusCode="404"
    redirect="~/Views/Shared/FileNotFound.cshtml" />

</customErrors>

2. HandleErrorAttribute enregistré en tant que filtre d'action global dans la classe FilterConfig comme suit

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

Si cela ne fonctionne pas, essayez de transférer la réponse en vérifiant les codes d'état comme suit dans le fichier Global.asax.

void Application_EndRequest(object sender, EventArgs e)
{
    if (Response.StatusCode == 401)
    {
        Response.ClearContent();
        Server.Transfer("~/Views/Shared/UnauthorizedAccess.cshtml");
    }
}
13
Nirmal Subedi

Je vais un peu hors sujet. Je pensais que c'était un peu important à expliquer.

enter image description here

Si vous faites attention à la partie mise en évidence ci-dessus. J'ai spécifié l'ordre du filtre d'action. Ceci décrit fondamentalement l'ordre d'exécution du filtre d'action. Il existe une situation dans laquelle plusieurs filtres d'action sont implémentés sur le contrôleur/la méthode d'action.

enter image description here

Cette image indique simplement que vous avez deux filtres d'action. OnActionExecution commencera à s'exécuter sur Priority et OnActionExecuted commencera de bas en haut. Cela signifie que, dans le cas de OnActionExecuted Action, le filtre ayant l'ordre le plus élevé sera exécuté en premier et dans le cas de OnActionExecuting, le filtre d'action ayant le plus faible ordre sera exécuté en premier. Exemple ci-dessous.

public class Filter1 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

// L'exécution commencera ici - 1

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {

// L'exécution va se déplacer ici - 5

        base.OnActionExecuted(filterContext);
    }
}

public class Filter2 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

// L'exécution va se déplacer ici - 2

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {

// L'exécution va se déplacer ici - 4

        base.OnActionExecuted(filterContext);
    }
}

[HandleError]
public class HomeController : Controller
{
    [Filter1(Order = 1)]
    [Filter2(Order = 2)]
    public ActionResult Index()
    {

// L'exécution va se déplacer ici - 3

        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }
}

Vous savez peut-être déjà qu'il existe différents types de filtres dans le cadre MVC. Ils sont énumérés ci-dessous.

  1. Filtres d'autorisation

  2. Filtres d'action

  3. Filtres de réponse/résultat

  4. Filtres d'exception

Dans chaque filtre, vous pouvez spécifier la propriété Order. Ceci décrit essentiellement l'ordre d'exécution des filtres d'action.

Retour à la requête d'origine

Cela fonctionne pour moi. C'est très facile et il n'est pas nécessaire d'envisager de modification dans Web.Config ou Enregistrer le filtre d'action dans le fichier Global.asax.

d'accord. Donc, je crée d'abord un simple Action Filter . Ceci gérera les requêtes Ajax et Non Ajax.

public class MyCustomErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        filterContext.ExceptionHandled = true;
        var debugModeMsg = filterContext.HttpContext.IsDebuggingEnabled
                               ? filterContext.Exception.Message +
                                 "\n" +
                                 filterContext.Exception.StackTrace
                               : "Your error message";

// C'est le cas lorsque vous devez gérer des requêtes Ajax

        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new
                {
                    error = true,
                    message = debugModeMsg
                }
            };
        }

// C'est le cas lorsque vous gérez une requête non Ajax

        else
        {
            var routeData = new RouteData();
            routeData.Values["controller"] = "Error";
            routeData.Values["action"] = "Error";
            routeData.DataTokens["area"] = "app";
            routeData.Values["exception"] = debugModeMsg;
            IController errorsController = new ErrorController();
            var exception = HttpContext.Current.Server.GetLastError();
            var httpException = exception as HttpException;
            if (httpException != null)
            {
                Response.StatusCode = httpException.GetHttpCode();
                switch (System.Web.HttpContext.Current.Response.StatusCode)
                {
                    case 404:
                        routeData.Values["action"] = "Http404";
                        break;
                }
            }

            var rc = new RequestContext
                         (
                             new HttpContextWrapper(HttpContext.Current),
                             routeData
                         );
            errorsController.Execute(rc);
        }
        base.OnException(filterContext);
    }
}

Maintenant, vous pouvez implémenter ce filtre d'action sur le contrôleur ainsi que sur l'action uniquement. Exemple:

enter image description here

J'espère que cela devrait vous aider.

6
Imad Alazani

Je ne suis pas sûr que cette réponse vous aidera, mais c'est un moyen simple ... J'ai placé error.html dans/et j'ai activé le mode pour les erreurs personnalisées dans la configuration Web et cela fonctionne parfaitement ...

  <system.web>
    <customErrors defaultRedirect="~/Error.html" mode="On" />
  </system.web>

this error.html est une page HTML de base avec tête et corps.

0
Venkata Tata

Créez un contrôleur Error Controller.

 public class ErrorController : Controller
    {
        //
        // GET: /Error/

        public ActionResult Index()
        {
            return View();
        }
}

Créez la vue Index pour l'action.

dans Web.config

<customErrors mode="On">
      <error statusCode="404" redirect="Error/Index"/>
</customErrors>

Lorsque vous gérez des erreurs dans votre code/logique

[HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start application.";

            return View("Index2");
        }
}

Attribut [HandleError] - sera redirigé vers la page Error.cshtml dans le dossier partagé.

0
chamara

Je veux partager mes connaissances après avoir étudié ce problème. Tous les commentaires qui aident à améliorer mes déclarations sont les bienvenus.

Dans ASP.NET MVC, trois couches traitent les demandes HTTP dans l'ordre suivant (la réponse est transférée dans l'ordre inverse):

  1. IIS (couche HTTP)

  2. ASP.NET (couche serveur)

  3. Contrôleur (couche MVC)

Toutes les couches ont une gestion des erreurs, mais chaque couche le fait différemment. Je vais commencer par IIS.

Couche IIS

L'exemple le plus simple de la façon dont IIS traite une erreur consiste à demander à votre serveur un fichier .html non existant à l'aide du navigateur. L'adresse doit ressembler à:

http: // localhost: 50123/this_does_not_exist.html

Notez le titre de l'onglet du navigateur, par exemple: IIS _ 10.0 Erreur détaillée - 404.0 - Introuvable.

Couche ASP.NET

Lorsque IIS reçoit une demande HTTP, si l'URL se termine par .aspx, il la transfère à ASP.NET car il est enregistré pour gérer cette extension. L'exemple le plus simple de la façon dont ASP.NET traite une erreur consiste à demander à votre serveur un fichier .aspx non existant à l'aide du navigateur. L'adresse doit ressembler à:

http: // localhost: 50123/this_does_not_exist.aspx

Notez les informations de version affichées en bas de la page, indiquant la version d'ASP.NET.

La balise customErrors a été créée à l'origine pour ASP.NET. Cela a un effet only lorsque la réponse est créée par le code interne ASP.NET. Cela signifie que cela n'affecte pas les réponses créées à partir du code de l'application. De plus, si la réponse renvoyée par ASP.NET n'a pas de contenu et comporte un code d'état d'erreur (4xx ou 5xx), alors IIS remplacera la réponse en fonction du code d'état. Je vais donner quelques exemples.

Si la méthode Page_Load contient Response.StatusCode = 404, le contenu est affiché normalement. Si le code supplémentaire Response.SuppressContent = true est ajouté, alors IIS intervient et traite l'erreur 404 de la même manière que lors de la demande "this_does_not_exist.html". Une réponse ASP.NET sans contenu ni code d'état 2xx n'est pas affectée.

Lorsque ASP.NET ne parvient pas à terminer la demande à l'aide du code de l'application, il la traite à l'aide du code interne. Voir les exemples suivants.

Si une URL ne peut pas être résolue, ASP.NET génère une réponse par lui-même. Par défaut, il crée une réponse 404 avec un corps HTML contenant des détails sur le problème. CustomErrors peut être utilisé pour créer une réponse 302 (Redirect) à la place. Toutefois, l'accès à une URL valide qui force ASP.NET à renvoyer une réponse 404 ne déclenche pas la redirection spécifiée par customErrors.

La même chose se produit lorsque ASP.NET intercepte une exception du code d'application. Par défaut, il crée une réponse 500 avec un corps HTML contenant des détails sur le code source à l'origine de l'exception. Là encore, customErrors peut être utilisé pour générer une réponse 302 (Redirect) à la place. Toutefois, la création d'une réponse 500 à partir du code d'application ne déclenche pas la redirection spécifiée par customErrors.

Les balises defaultRedirect et error sont assez simples à comprendre compte tenu de ce que je viens de dire. La balise error est utilisée pour spécifier une redirection pour un code d'état spécifique. S'il n'y a pas d'étiquette d'erreur correspondante, alors defaultRedirect sera utilisé. L'URL de redirection peut pointer sur tout ce que le serveur peut gérer, y compris l'action du contrôleur.

Couche MVC

Avec ASP.NET MVC, les choses se compliquent. Premièrement, il peut y avoir deux fichiers "Web.config", un dans la racine et un dans le dossier Vues. Je tiens à noter que le "Web.config" par défaut de Views fait deux choses d'intérêt pour ce fil:

  • Il désactive la gestion des URL dans les fichiers .cshtml (pages Web: Activé défini sur false)
  • Il empêche l'accès direct à tout contenu du dossier Views (BlockViewHandler).

Dans le cas d'ASP.NET MVC, le HandleErrorAttribute peut être ajouté à GlobalFilters , qui prend également en compte la valeur de mode de l'attribut customErrors de la racine "Web.config". Plus précisément, lorsque le paramètre est défini sur Activé, il active la gestion des erreurs sur la couche MVC pour les exceptions non détectées dans le code contrôleur/action. Plutôt que de les transférer à ASP.NET, il affiche par défaut Views/Shared/Error.cshtml. Cela peut être changé en définissant la propriété View de HandleErrorAttribute.

La gestion des erreurs sur la couche MVC commence après la résolution du contrôleur/de l'action, en fonction de l'URL de la demande. Par exemple, une demande qui ne respecte pas les paramètres de l'action est gérée par la couche MVC. Cependant, si une demande POST n'a aucun contrôleur/action correspondant capable de gérer le POST, l'erreur est gérée au niveau de la couche ASP.NET.

J'ai utilisé ASP.NET MVC 5 pour les tests. Il semble n'y avoir aucune différence entre IIS et IIS _ Express en ce qui concerne le traitement des erreurs.

Réponse

La seule raison pour laquelle je peux penser à la raison pour laquelle customErrors n'est pas prise en compte pour les codes d'état non-500 est qu'ils ont été créés avec HttpStatusCodeResponse. Dans ce cas, la réponse est créée par le code de l'application et n'est pas gérée par ASP.NET, mais plutôt par IIS. À ce stade, la configuration d'une page alternative est inutile. Voici un exemple de code reproduisant ce problème:

public ActionResult Unhandled404Error()
{
    return new HttpStatusCodeResult(HttpStatusCode.NotFound);
}

Dans un tel scénario, je vous recommande d'implémenter un ActionFilterAttribute qui remplacera OnResultExecuted et procédera comme suit:

int statusCode = filterContext.HttpContext.Response.StatusCode;
if(statusCode >= 400)
{
    filterContext.HttpContext.Response.Clear();
    filterContext.HttpContext.Response.Redirect("/Home/Index");
}

ActionFilterAttribute implémenté doit être ajouté à GlobalFilters.

0
MatrixRonny