39 votes

Comment implémenter une gestion appropriée des erreurs HTTP dans .NET MVC 2?

J'ai eu du mal toute la journée à mettre en œuvre la gestion des erreurs dans mon application ASP.NET MVC 2. J'ai étudié plusieurs techniques, mais aucune ne fonctionne correctement. J'utilise MVC2 et .NET 4.0 (j'ai commencé le projet avant la sortie de MVC3 ; nous ferons la mise à jour après la livraison de notre première version).

À ce stade, je serais heureux de gérer correctement les erreurs 404 et 500 -- un 403 (autorisation requise) serait génial aussi, suivi de diverses autres réponses spécifiques. Actuellement, je reçois soit toutes les 404, toutes les 500, toutes les 302 avant la 404, ou toutes les 302 avant la 500.

Voici mes exigences (qui devraient être assez proches des exigences de base de HTTP) :

  • Si une ressource n'est pas trouvée, renvoyer un 404, et afficher une page spécifique au 404 avec l'URL demandée. NE PAS renvoyer un code de réponse intermédiaire comme un 302. Idéalement, conserver l'URL demandée, plutôt que d'afficher une nouvelle URL comme /Error/NotFound -- mais si cette dernière s'affiche, assurez-vous que nous n'avons pas renvoyé de réponse de redirection pour l'obtenir.

  • Si une erreur interne du serveur s'est produite, renvoyer un 500, et afficher une erreur spécifique au 500 avec une indication de ce qui s'est mal passé. Encore une fois, ne renvoyez pas de code de réponse intermédiaire, et idéalement ne modifiez pas l'URL.

Voici ce que je considérerais comme un 404 :

  1. Fichier statique introuvable : /Contenu/non-existent-dir/non-existent-file.txt
  2. Contrôleur introuvable : /non-existent-controller/Foo/666
  3. Contrôleur trouvé, mais action introuvable : /Accueil/non-existent-action/666
  4. Contrôleur et action trouvés, mais l'action ne peut pas trouver l'objet demandé : /Accueil/Connexion/non-existent-id

Voici ce que je considérerais comme un 500 :

  1. Postez une mauvaise valeur : POST /Utilisateur/Nouveau/nom-d-utilisateur-trop-long-pour-la-contrainte-de-colonne-db
  2. Problème non lié aux données, comme un point de terminaison de service Web qui ne répond pas

Certains de ces problèmes doivent être identifiés par des contrôleurs ou des modèles spécifiques, puis les contrôleurs doivent déclencher les HttpException appropriées. Le reste devrait être traité de manière plus générique.

Pour le cas 404 n°2, j'ai essayé d'utiliser une Factory de contrôleurs personnalisée pour renvoyer un 404 si le contrôleur ne peut pas être trouvé. Pour le cas 404 n°3, j'ai essayé d'utiliser un contrôleur de base personnalisé pour remplacer HandleUnknownAction et renvoyer un 404.

Dans les deux cas, je reçois un 302 avant le 404. Et, je ne reçois jamais d'erreurs 500 ; si je modifie Web.config pour mettre une erreur de frappe dans mon point de terminaison de service Web, je reçois toujours un 302, puis un 404 disant que l'URL (contrôleur/action) qui utilise le service Web ne peut pas être trouvé. Je reçois également l'URL demandée comme paramètre de chaîne de requête (non désiré) : /Error/NotFound?aspxerrorpath=/Accueil/non-existent-action

Les deux techniques viennent de http://www.niksmit.com/wp/?p=17 (Comment obtenir des pages d'erreur 404 normales (Page non trouvée) avec ASP.Net MVC), mentionnées sur http://richarddingwall.name/2008/08/17/strategies-for-resource-based-404-errors-in-aspnet-mvc/

Si dans Web.config j'ai , je reçois le code de réponse approprié, mais mon contrôleur d'erreur n'est jamais appelé. En retirant l'attribut redirectMode, je reçois les vues d'erreur MVC, mais avec un 302 intermédiaire et une URL modifiée -- et toujours le même contrôleur (Unknown = 500 ; si je le change en NotFound tout semble être un 404).

Voici quelques-unes des autres choses que j'ai lues et essayé de mettre en œuvre :

.. ainsi qu'une multitude de publications sur StackOverflow.

Il me semble que ce type de gestion des erreurs est assez fondamental pour les applications Web, et le framework MVC devrait avoir des configurations par défaut qui le font automatiquement, et permettent aux gens de l'étendre pour qu'il fonctionne différemment. Peut-être le feront-ils dans une future version. En attendant, quelqu'un peut-il me donner des détails complets sur comment implémenter correctement les réponses HTTP ?

49voto

Darin Dimitrov Points 528142

Voici une technique que vous pourriez utiliser. Définissez un ErrorsController qui servira les pages d'erreur :

public class ErrorsController : Controller
{
    public ActionResult Http404()
    {
        Response.StatusCode = 404;
        return Content("404", "text/plain");
    }

    public ActionResult Http500()
    {
        Response.StatusCode = 500;
        return Content("500", "text/plain");
    }

    public ActionResult Http403()
    {
        Response.StatusCode = 403;
        return Content("403", "text/plain");
    }
}

et ensuite dans Global.asax, vous pourriez vous abonner à l'événement Application_Error où vous pourriez enregistrer l'exception et exécuter l'action correspondante du ErrorsController :

protected void Application_Error(object sender, EventArgs e)
{
    var app = (MvcApplication)sender;
    var context = app.Context;
    var ex = app.Server.GetLastError();
    context.Response.Clear();
    context.ClearError();
    var httpException = ex as HttpException;

    var routeData = new RouteData();
    routeData.Values["controller"] = "errors";
    routeData.Values["exception"] = ex;
    routeData.Values["action"] = "http500";
    if (httpException != null)
    {
        switch (httpException.GetHttpCode())
        {
            case 404:
                routeData.Values["action"] = "http404";
                break;
            case 403:
                routeData.Values["action"] = "http403";
                break;
            case 500:
                routeData.Values["action"] = "http500";
                break;
        }
    }
    IController controller = new ErrorsController();
    controller.Execute(new RequestContext(new HttpContextWrapper(context), routeData));
}

Et maintenant tout ce qu'il vous reste à faire est de commencer à lancer des exceptions appropriées :

public class HomeController : Controller
{
    public ActionResult Index()
    {
        throw new HttpException(404, "NotFound");
    }
}

4voto

Hector Correa Points 10408

Pour les erreurs HTTP 404 (sans redirection), consultez mon article de blog sur le sujet. Cela pourrait vous donner quelques bonnes idées:

http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16

0voto

Ceci est une question très ancienne, mais j'ai pensé que cela valait la peine de vous présenter une façon beaucoup plus propre de gérer les exceptions Http que j'ai vue dans la réponse de "Jesse Webb".

La solution consiste à utiliser l'élément httpErrors de la section system.webServer:

Vous pouvez également enregistrer toutes les exceptions de cette manière. "Lisez la "réponse de Jesse Webb"".

Cela semble vraiment beaucoup plus propre et fonctionne aussi bien que toute autre solution (sans redirection).

Note : Ceci fonctionne uniquement sur IIS 7 et plus récent. (En raison de l'élément httpErrors qui a été ajouté récemment.

0voto

JK. Points 5517

Cela ne répond pas à votre question, mais il est important de noter que le statut HTTP 500 indique qu'il y a eu une erreur sur le serveur, donc votre exemple :

POST /Utilisateur/Nouveau/nom-utilisateur-trop-long-pour-la-contrainte-de-colonne-db

N'est pas une raison valable pour renvoyer une erreur 500, c'est un problème de validation des données et cela devrait être géré par les annotations de données MVC ou un framework de validation jQuery, etc. Afficher un message d'erreur à côté de la zone de texte disant "Nom d'utilisateur trop long" est bien mieux.

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