web-dev-qa-db-fra.com

ASP.NET MVC 404 Gestion des erreurs

Duplicate possible:
Comment puis-je gérer correctement 404 dans ASP.NET MVC?

J'ai apporté les modifications décrites dans gestionnaire d'erreurs Http 404 dans Asp.Net MVC (RC 5) et je reçois toujours la page d'erreur 404 standard. Dois-je changer quelque chose dans IIS?

129
Clearly

Encore une autre solution.

Ajoutez ErrorControllers ou une page statique avec des informations d'erreur 404.

Modifiez votre web.config (en cas de contrôleur).

<system.web>
    <customErrors mode="On" >
       <error statusCode="404" redirect="~/Errors/Error404" />
    </customErrors>
</system.web>

Ou en cas de page statique

<system.web>
    <customErrors mode="On" >
        <error statusCode="404" redirect="~/Static404.html" />
    </customErrors>
</system.web>

Cela gérera les itinéraires manqués et les actions manquées.

142
Mike Chaliy

J'ai étudié BEAUCOUP sur la façon de gérer correctement les 404 dans MVC (plus précisément MVC3), et cela, à mon humble avis est la meilleure solution que j'ai trouvée:

Dans global.asax:

public class MvcApplication : HttpApplication
{
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            Response.Clear();

            var rd = new RouteData();
            rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
            rd.Values["controller"] = "Errors";
            rd.Values["action"] = "NotFound";

            IController c = new ErrorsController();
            c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
        }
    }
}

ErrorsController:

public sealed class ErrorsController : Controller
{
    public ActionResult NotFound()
    {
        ActionResult result;

        object model = Request.Url.PathAndQuery;

        if (!Request.IsAjaxRequest())
            result = View(model);
        else
            result = PartialView("_NotFound", model);

        return result;
    }
}

Modifier:

Si vous utilisez IoC (par exemple, AutoFac), vous devez créer votre contrôleur en utilisant:

var rc = new RequestContext(new HttpContextWrapper(Context), rd);
var c = ControllerBuilder.Current.GetControllerFactory().CreateController(rc, "Errors");
c.Execute(rc);

Au lieu de

IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));

(Facultatif)

Explication:

Il y a 6 scénarios auxquels je peux penser où une application ASP.NET MVC3 peut générer des 404.

Généré par ASP.NET:

  • Scénario 1: L'URL ne correspond à aucune route du tableau des routes.

Généré par ASP.NET MVC:

  • Scénario 2: L'URL correspond à une route, mais spécifie un contrôleur qui n'existe pas.

  • Scénario 3: L'URL correspond à une route, mais spécifie une action qui n'existe pas.

Généré manuellement:

  • Scénario 4: Une action renvoie un HttpNotFoundResult en utilisant la méthode HttpNotFound ().

  • Scénario 5: Une action lève une exception HttpException avec le code d'état 404.

  • Scénario 6: Une action modifie manuellement la propriété Response.StatusCode sur 404.

Objectifs

  • (A) Afficher une page d'erreur 404 personnalisée à l'utilisateur.

  • (B) Conservez le code d'état 404 sur la réponse du client (particulièrement important pour le référencement).

  • (C) Envoyez la réponse directement, sans impliquer de redirection 302.

Tentative de solution: erreurs personnalisées

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customErrors>
</system.web>

Problèmes avec cette solution:

  • Ne répond pas à l'objectif (A) dans les scénarios (1), (4), (6).
  • Ne se conforme pas à l'objectif (B) automatiquement. Il doit être programmé manuellement.
  • Non conforme à l'objectif (C).

Tentative de solution: Erreurs HTTP

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Problèmes avec cette solution:

  • Ne fonctionne que sur IIS 7+.
  • Ne répond pas à l'objectif (A) dans les scénarios (2), (3) et (5).
  • Ne se conforme pas à l'objectif (B) automatiquement. Il doit être programmé manuellement.

Tentative de solution: Erreurs HTTP avec Replace

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Problèmes avec cette solution:

  • Ne fonctionne que sur IIS 7+.
  • Ne se conforme pas à l'objectif (B) automatiquement. Il doit être programmé manuellement.
  • Il masque les exceptions http au niveau de l'application. Par exemple. ne peut pas utiliser la section customErrors, System.Web.Mvc.HandleErrorAttribute, etc. Elle ne peut pas uniquement afficher les pages d'erreur génériques.

Solution Essayez customErrors et les erreurs HTTP

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

et

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Problèmes avec cette solution:

  • Ne fonctionne que sur IIS 7+.
  • Ne se conforme pas à l'objectif (B) automatiquement. Il doit être programmé manuellement.
  • Ne répond pas à l'objectif (C) dans les scénarios (2), (3) et (5).

Les personnes qui en avaient déjà souffert auparavant ont même essayé de créer leurs propres bibliothèques (voir http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html ). Mais la solution précédente semble couvrir tous les scénarios sans la complexité d'utiliser une bibliothèque externe.

360
Marco

La réponse de Marco est la meilleure solution. J'avais besoin de contrôler mon traitement des erreurs, et je veux dire vraiment le contrôler. Bien entendu, j'ai un peu étendu la solution et créé un système complet de gestion des erreurs qui gère tout. J'ai également entendu parler de cette solution dans d'autres blogs et cela semble très acceptable pour la plupart des développeurs avancés.

Voici le code final que j'utilise:

protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            var exception = Server.GetLastError();
            var httpException = exception as HttpException;
            Response.Clear();
            Server.ClearError();
            var routeData = new RouteData();
            routeData.Values["controller"] = "ErrorManager";
            routeData.Values["action"] = "Fire404Error";
            routeData.Values["exception"] = exception;
            Response.StatusCode = 500;

            if (httpException != null)
            {
                Response.StatusCode = httpException.GetHttpCode();
                switch (Response.StatusCode)
                {
                    case 404:
                        routeData.Values["action"] = "Fire404Error";
                        break;
                }
            }
            // Avoid IIS7 getting in the middle
            Response.TrySkipIisCustomErrors = true;
            IController errormanagerController = new ErrorManagerController();
            HttpContextWrapper wrapper = new HttpContextWrapper(Context);
            var rc = new RequestContext(wrapper, routeData);
            errormanagerController.Execute(rc);
        }
    }

et dans mon ErrorManagerController:

        public void Fire404Error(HttpException exception)
    {
        //you can place any other error handling code here
        throw new PageNotFoundException("page or resource");
    }

Maintenant, dans mon action, je lance une exception personnalisée que j'ai créée. Et mon contrôleur hérite d'une classe de contrôleur personnalisée que j'ai créée. Le contrôleur de base personnalisé a été créé pour remplacer le traitement des erreurs. Voici ma classe de contrôleur de base personnalisée:

public class MyBasePageController : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        filterContext.GetType();
        filterContext.ExceptionHandled = true;
        this.View("ErrorManager", filterContext).ExecuteResult(this.ControllerContext);
        base.OnException(filterContext);
    }
}

Le "ErrorManager" dans le code ci-dessus est simplement une vue qui utilise un modèle basé sur ExceptionContext

Ma solution fonctionne parfaitement et je suis capable de gérer TOUTE erreur sur mon site Web et d’afficher différents messages en fonction de TOUT type d’exception.

5
Yousi

On dirait que c'est le meilleur moyen de tout attraper.

Comment puis-je gérer correctement 404 dans ASP.NET MVC?

4
Clearly

Dans IIS, vous pouvez spécifier une redirection vers "certaines" pages en fonction du code d'erreur. Dans votre exemple, vous pouvez configurer 404 -> Votre page d’erreur 404 personnalisée.

1
J.W.

Ce que je peux recommander est de regarder sur FilterAttribute. Par exemple, MVC a déjà HandleErrorAttribute. Vous pouvez le personnaliser pour ne gérer que 404. Si vous êtes intéressé, je vais regarder par exemple.

BTW

La solution (avec le dernier itinéraire) que vous avez acceptée à la question précédente ne fonctionne pas dans la plupart des situations. La deuxième solution avec HandleUnknownAction fonctionnera mais nécessitera cette modification dans chaque contrôleur ou un seul contrôleur de base.

Mon choix est une solution avec HandleUnknownAction.

1
Mike Chaliy