446 votes

Comment pourrais-je ?

Je suis juste de commencer sur ASP.NET MVC afin de garder avec moi. J'ai cherché autour de ce site et les différents autres et avoir vu quelques implémentations de cette.

EDIT: j'ai oublié de mentionner que je suis en utilisant la RC2

En utilisant le Routage d'URL:

routes.MapRoute(
    "Error",
     "{*url}",
     new { controller = "Errors", action = "NotFound" }  // 404s
);

Ce qui précède semble prendre soin de les demandes de ce genre (en supposant que la route par défaut des tables de configuration initiale de projet MVC): "/bla/bla/bla/bla"

Primordial HandleUnknownAction() dans le contrôleur lui-même:

// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
    ViewData["actionName"] = actionName;
    View("NotFound").ExecuteResult(this.ControllerContext);
}  

Cependant, les stratégies précédentes de ne pas traiter une requête à un Mauvais/commande Inconnue. Par exemple, je n'ai pas de "/IDoNotExist", si je demande cela, je reçois le générique de la page 404 de du serveur web et non pas mon erreur 404 si j'utilise le routage + remplacer.

Donc, finalement, ma question est: Est-il possible d'attraper ce type de demande à l'aide d'une route ou autre chose dans le MVC framework lui-même?

OU devrais-je juste valeur par défaut à l'aide de Web.Config customErrors que ma 404 et oublier tout ça? Je suppose que si je vais avec customErrors je vais avoir à stocker le générique de la page 404 en dehors de /points de Vue en raison de la Web.Config restrictions sur l'accès direct. De toute façon toute les meilleures pratiques ou d'orientation est apprécié.

279voto

Shay Jacoby Points 1819

Le code est pris à partir de http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx et dans les œuvres ASP.net MVC 1.0 ainsi

Voici comment je gère http exceptions:

protected void Application_Error(object sender, EventArgs e)
{
   Exception exception = Server.GetLastError();
   // Log the exception.

   ILogger logger = Container.Resolve<ILogger>();
   logger.Error(exception);

   Response.Clear();

   HttpException httpException = exception as HttpException;

   RouteData routeData = new RouteData();
   routeData.Values.Add("controller", "Error");

   if (httpException == null)
   {
       routeData.Values.Add("action", "Index");
   }
   else //It's an Http Exception, Let's handle it.
   {
       switch (httpException.GetHttpCode())
       {
          case 404:
              // Page not found.
              routeData.Values.Add("action", "HttpError404");
              break;
          case 500:
              // Server error.
              routeData.Values.Add("action", "HttpError500");
              break;

           // Here you can handle Views to other error codes.
           // I choose a General error template  
           default:
              routeData.Values.Add("action", "General");
              break;
      }
  }           

  // Pass exception details to the target error View.
  routeData.Values.Add("error", exception);

  // Clear the error on server.
  Server.ClearError();

  // Avoid IIS7 getting in the middle
  Response.TrySkipIisCustomErrors = true; 

  // Call target Controller and pass the routeData.
  IController errorController = new ErrorController();
  errorController.Execute(new RequestContext(    
       new HttpContextWrapper(Context), routeData));
}

258voto

cottsak Points 5490

Exigences pour les 404

Ce qui suit sont mes besoins pour une 404 solution et ci-dessous, je montre comment je mettre en œuvre:

  • Je veux gérer des itinéraires correspondant à de mauvaises actions
  • Je veux gérer des itinéraires correspondant avec un mauvais contrôleurs
  • Je veux poignée de non-correspondance des routes (arbitraire url que mon application ne peut pas comprendre) - je ne veux pas de ces bouillonnant sur le Global.asax ou IIS, car alors je ne peux pas rediriger sur mon MVC application correctement
  • Je veux un moyen de gérer de la même manière que ci-dessus, la coutume erreur 404 - comme quand un ID est soumis à un objet qui n'existe pas (peut être supprimé)
  • Je veux que tous mes erreur 404 de retour d'un MVC vue (pas une page statique) à laquelle je pompe le plus de données, plus tard, si nécessaire (bon 404 dessins) et ils doivent retourner le code d'état HTTP 404

Solution

Je pense que vous devriez économiser Application_Error dans le monde.asax pour les choses les plus élevées, comme les exceptions non gérées et l'exploitation forestière (comme Shay Jacoby la réponse de la montre) mais pas de 404 de la manipulation. C'est pourquoi ma proposition tient le 404 choses de la Mondiale.asax fichier.

Étape 1: Avoir un lieu commun pour erreur 404 logique

C'est une bonne idée pour la maintenabilité. Utiliser un ErrorController de sorte que les futures améliorations de votre bien conçu, la page d'erreur 404 peut s'adapter facilement. Aussi, assurez-vous que votre réponse a le code 404!

public class ErrorController : MyController
{
    #region Http404

    public ActionResult Http404(string url)
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        var model = new NotFoundViewModel();
        // If the url is relative ('NotFound' route) then replace with Requested path
        model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
            Request.Url.OriginalString : url;
        // Dont get the user stuck in a 'retry loop' by
        // allowing the Referrer to be the same as the Request
        model.ReferrerUrl = Request.UrlReferrer != null &&
            Request.UrlReferrer.OriginalString != model.RequestedUrl ?
            Request.UrlReferrer.OriginalString : null;

        // TODO: insert ILogger here

        return View("NotFound", model);
    }
    public class NotFoundViewModel
    {
        public string RequestedUrl { get; set; }
        public string ReferrerUrl { get; set; }
    }

    #endregion
}

Étape 2: Utilisation d'une base de Contrôleur de classe de sorte que vous pouvez facilement appeler votre 404 personnalisée d'action et le fil jusqu' HandleUnknownAction

Une erreur 404 dans ASP.NET MVC doivent être pris à un certain nombre d'endroits. La première est HandleUnknownAction.

L' InvokeHttp404 méthode permet de créer un espace commun pour le re-routage de l' ErrorController et notre nouveau Http404 action. Pensez à SEC!

public abstract class MyController : Controller
{
    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        // If controller is ErrorController dont 'nest' exceptions
        if (this.GetType() != typeof(ErrorController))
            this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

Étape 3: Utiliser l'Injection de Dépendance dans votre Contrôleur de l'Usine et le fil jusqu'404 HttpExceptions

Comme (il n'a pas à être StructureMap):

MVC1.0 exemple:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }
}

MVC2.0 exemple:

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == 404)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }

Je pense que c'est mieux pour attraper les erreurs de plus près d'où ils proviennent. C'est pourquoi je préfère le au-dessus de l' Application_Error gestionnaire.

C'est le deuxième endroit pour attraper une erreur 404.

Étape 4: Ajouter un NotFound de l'itinéraire Global.asax pour les url qui ne parviennent pas à être analysé dans votre application

Cette route devrait point à notre - Http404 action. Avis de l' url param sera une url relative, car le moteur de routage est le décapage de la partie domaine ici? C'est pourquoi nous avons tous que la condition de l'url de la logique à l'Étape 1.

        routes.MapRoute("NotFound", "{*url}", 
            new { controller = "Error", action = "Http404" });

C'est la troisième et dernière place pour attraper une erreur 404 dans une application MVC que vous n'avez pas appeler vous-même. Si vous n'avez pas attraper inégalée routes d'ici, puis MVC va passer le problème à ASP.NET (à l'échelle Mondiale.asax) et vous ne voulez pas vraiment que dans cette situation.

Étape 5: Enfin, invoquer une erreur 404 lorsque votre application ne peut pas trouver quelque chose

Comme quand une mauvaise ID est soumis à mes Prêts contrôleur (dérive de MyController):

    //
    // GET: /Detail/ID

    public ActionResult Detail(int ID)
    {
        Loan loan = this._svc.GetLoans().WithID(ID);
        if (loan == null)
            return this.InvokeHttp404(HttpContext);
        else
            return View(loan);
    }

Ce serait bien si tout cela pouvait être accroché dans moins d'endroits avec moins de code, mais je pense que cette solution est plus facile à gérer, plus vérifiable et assez pragmatique.

Merci pour les commentaires jusqu'à présent. J'aimerais obtenir plus.

REMARQUE: Ce qui a été modifié de manière significative à partir de ma réponse originale à cette question, mais le but et les exigences sont les mêmes - c'est pourquoi je n'ai pas ajouté une nouvelle réponse

241voto

Pavel Chuchuva Points 12220

ASP.NET MVC ne supporte pas les pages d'erreur 404 personnalisées très bien. Personnalisé contrôleur de l'usine, de fourre-tout de l'itinéraire, de la base de contrôleur de classe avec HandleUnknownAction - argh!

IIS pages d'erreur personnalisées sont la meilleure alternative pour l'instant:

web.config

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

ErrorController

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View();
    }
}

Exemple De Projet

Test404.zip

154voto

Pure.Krome Points 28473

Réponse rapide / TL;DR

enter image description here

Pour les paresseux gars là-bas:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

Ensuite, supprimez cette ligne de global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

Et ce n'est que pour IIS7+ et IIS Express.

Si vous êtes à l'aide de Cassini .. bien .. euh .. er.. maladroit ...


De Long, a expliqué répondre

Je sais que cela a été répondu. Mais la réponse est très SIMPLE (bravo à David Fowler et Damian Edwards pour vraiment répondre à cette).

Il n'y a pas besoin de faire quelque chose de personnalisé.

Pour ASP.NET MVC3, tous les morceaux sont là.

Étape 1 -> mise à Jour de votre site web.config en DEUX endroits.

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

et

<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/NotFound" responseMode="ExecuteURL" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="500" path="/ServerError" responseMode="ExecuteURL" />
    </httpErrors>    

...
<system.webServer>
...
</system.web>

Maintenant, prenez soin de noter les ROUTES, j'ai décidé d'utiliser. Vous pouvez utiliser n'importe quoi, mais mes itinéraires sont

  • /NotFound <- pour un 404 not found, page d'erreur.
  • /ServerError <- pour toute autre erreur, comprennent les erreurs qui se produisent dans mon code. c'est une Erreur de Serveur Interne 500

Voir comment la première section en <system.web> a seulement une entrée personnalisée? L' statusCode="404" entrée? Je ne l'ai inscrit un code d'état, parce que toutes les autres erreurs, y compris l' 500 Server Error (ie. ces satanés erreur qui se produit lorsque votre code a un bug et plantages de la demande de l'utilisateur) .. tous les autres erreurs sont gérées par le paramètre defaultRedirect="/ServerError" .. ce qui est dit, si vous n'êtes pas une erreur 404 page non trouvée, alors s'il vous plaît rendez-vous sur l'itinéraire /ServerError.

Ok. c'est hors de la voie.. maintenant à mes itinéraires répertoriés en global.asax

Étape 2 - Création d'itinéraires Mondiale.asax

Voici mon itinéraire complet de la section..

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("{*favicon}", new {favicon = @"(.*/)?favicon.ico(/.*)?"});

    routes.MapRoute(
        "Error - 404",
        "NotFound",
        new { controller = "Error", action = "NotFound" }
        );

    routes.MapRoute(
        "Error - 500",
        "ServerError",
        new { controller = "Error", action = "ServerError"}
        );

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
        );
}

Que des listes deux ignorer les routes -> axd's et favicons (ooo! bonus ignorer la route, pour vous!) Ensuite (et l'ordre est IMPÉRATIF ICI), j'ai mes deux explicite erreur de manipulation des routes .. suivie par tous les autres routes. Dans ce cas, la valeur par défaut. Bien sûr, j'en ai plus, mais c'est spécial pour mon site web. Assurez-vous simplement que l'erreur routes sont en haut de la liste. L'ordre est impératif.

Enfin, alors que nous sommes à l'intérieur de notre - global.asax le fichier, nous ne sommes PAS à l'échelle mondiale enregistrer le HandleError attribut. Non, non, non, monsieur. Nadda. Nope. Nien. Négatif. Noooooooooo...

Supprimer cette ligne d' global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

Étape 3 - Créer le contrôleur avec les méthodes d'action

Maintenant .. nous ajouter un contrôleur à deux méthodes d'action ...

public class ErrorController : Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ActionResult ServerError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        // Todo: Pass the exception into the view model, which you can make.
        //       That's an exercise, dear reader, for -you-.
        //       In case u want to pass it to the view, if you're admin, etc.
        // if (User.IsAdmin) // <-- I just made that up :) U get the idea...
        // {
        //     var exception = Server.GetLastError();
        //     // etc..
        // }

        return View();
    }

    // Shhh .. secret test method .. ooOOooOooOOOooohhhhhhhh
    public ActionResult ThrowError()
    {
        throw new NotImplementedException("Pew ^ Pew");
    }
}

Ok, permet de vérifier cela. Tout d'abord, il n'y a PAS d' [HandleError] attribut ici. Pourquoi? Parce que le construit en ASP.NET - cadre est déjà en cours de traitement des erreurs ET nous avons spécifié toute la merde que nous devons faire pour gérer une erreur :) C'est dans cette méthode!

Ensuite, j'ai les deux méthodes d'action. Rien de difficile. Si u veut montrer toute exception info, alors u peut utiliser Server.GetLastError() pour obtenir cette info.

Bonus WTF: Oui, j'ai fait une troisième méthode d'action, afin de tester la gestion des erreurs.

Étape 4 - Créer les Vues

Et enfin, la création de deux points de vue. Mettre em dans la vue normale spot, pour ce contrôleur.

enter image description here

Bonus commentaires

  • Vous n'avez pas besoin d' Application_Error(object sender, EventArgs e)
  • Les étapes ci-dessus tous les travaux 100% parfaitement avec Elmah. Elmah fraking wroxs!

Et cela, mes amis, doit être.

Maintenant, bravo pour la lecture de ce bien et ont une Licorne comme prix!

enter image description here

90voto

Marco Points 2652

Je l'ai étudié BEAUCOUP de choses sur la façon de gérer correctement une erreur 404 en MVC (plus précisément MVC3), et ce, à mon humble avis est la meilleure solution que j'ai trouvé:

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

(Facultatif)

Explication:

Autant que je sache, il y a 6 différents cas qu'un ASP.NET MVC3 les applications peuvent générer une erreur 404.

(Généré automatiquement par ASP.NET Cadre:)

(1) Une URL ne trouve pas de correspondance dans la table de routage.

(Généré automatiquement par ASP.NET Framework MVC:)

(2) Une URL trouve une correspondance dans la table de routage, mais spécifie inexistante du contrôleur.

(3) Une URL trouve une correspondance dans la table de routage, mais spécifie un non-existant à l'action.

(Généré manuellement:)

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

(5) Une action déclenche une HttpException avec le code d'état 404.

(6) actions modifie manuellement la Réponse.StatusCode propriété à 404.

Normalement, vous voulez atteindre 3 objectifs:

(1) Afficher une page erreur 404 personnalisée à l'utilisateur.

(2) Maintenir le code d'état 404 sur la réponse du client (spécialement important pour le SEO).

(3) Envoyer la réponse directement, sans impliquer une redirection 302.

Il existe différentes façons pour essayer de faire ceci:

(1)

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

Des problèmes avec cette solution:

  1. N'est pas conforme à l'objectif (1) dans les cas (1), (4), (6).
  2. N'est pas conforme à l'objectif (2) automatiquement. Elle doit être programmée manuellement.
  3. N'est pas conforme à l'objectif (3).

(2)

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

Des problèmes avec cette solution:

  1. Ne fonctionne que sur IIS 7+.
  2. N'est pas conforme à l'objectif (1) dans les cas (2), (3), (5).
  3. N'est pas conforme à l'objectif (2) automatiquement. Elle doit être programmée manuellement.

(3)

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

Des problèmes avec cette solution:

  1. Ne fonctionne que sur IIS 7+.
  2. N'est pas conforme à l'objectif (2) automatiquement. Elle doit être programmée manuellement.
  3. Il obscurcit le niveau d'application de http exceptions. E. g. ne pouvez pas utiliser customErrors section Système.Web.Mvc.HandleErrorAttribute, etc. Il ne peut pas afficher uniquement les pages d'erreur génériques.

(4)

<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>

Des problèmes avec cette solution:

  1. Ne fonctionne que sur IIS 7+.
  2. N'est pas conforme à l'objectif (2) automatiquement. Elle doit être programmée manuellement.
  3. N'est pas conforme à l'objectif (3) dans les cas (2), (3), (5).

Les gens qui ont troublé, avant 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 cas sans la complexité de l'utilisation d'une bibliothèque externe.

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