web-dev-qa-db-fra.com

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

Toute la journée, j'ai eu du mal à mettre en œuvre la gestion des erreurs dans mon application ASP.NET MVC 2. J'ai étudié diverses techniques, mais aucune ne fonctionne correctement. J'utilise MVC2 et .NET 4.0 (le projet a été lancé avant la sortie de MVC3; nous mettrons à niveau après la livraison de notre version initiale).

À ce stade, je serai heureux de traiter correctement les erreurs 404 et 500. 403 (autorisation requise) serait également utile, suivi de diverses autres réponses spécifiques. À l’heure actuelle, je reçois soit tous les 404, tous les 500, tous les 302 avant les 404, ou tous les 302 avant les 500.

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

  • Si une ressource n'est pas trouvée, lancez un 404 et affichez une page spécifique à 404 avec l'URL demandée. NE retournez PAS un code de réponse intermédiaire tel que 302. Idéalement, conservez l'URL demandée plutôt que d'afficher une nouvelle URL telle que /Error/NotFound - mais si ce dernier s'affiche, assurez-vous de ne pas avoir renvoyé de réponse de redirection pour l'obtenir.

  • Si une erreur interne du serveur s'est produite, lancez un 500 et affichez une erreur spécifique à 500 avec une indication de ce qui s'est mal passé. Encore une fois, ne retournez 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 non trouvé: /Content/non-existent-dir/non-existent-file.txt
  2. Contrôleur non trouvé: /non-existent-controller/Foo/666
  3. Contrôleur trouvé, mais action introuvable: /Home/non-existent-action/666
  4. Le contrôleur et l'action ont été trouvés, mais l'action ne trouve pas l'objet demandé: /Home/Login/non-existent-id

Voici ce que je considérerais comme un 500:

  1. Postez une mauvaise valeur: POST /User/New/new-user-name-too-long-for-db-column-constraint
  2. Problème non lié aux données, tel qu'un point de terminaison de service Web ne répondant pas

Certains de ces problèmes doivent être identifiés par des contrôleurs ou des modèles spécifiques, qui doivent ensuite émettre l'exception HttpException appropriée. Le reste devrait être traité de manière plus générique.

Pour le cas 404 n ° 2, j'ai essayé d'utiliser un ControllerFactory personnalisé pour lancer un 404 si le contrôleur est introuvable. Pour le cas n ° 3 de 404, j'ai essayé d'utiliser un contrôleur de base personnalisé pour remplacer HandleUnknownAction et lancer un 404.

Dans les deux cas, je reçois un 302 avant le 404. Et je ne reçois jamais 500 erreurs; Si je modifie Web.config pour qu'il mette une faute de frappe dans le noeud final de mon service Web, j'obtiens toujours un 302, puis un 404 indiquant l'URL (contrôleur/action) que utilise le service Web est introuvable. J'obtiens également l'URL demandée sous forme de paramètre de requête (n indésirable): /Error/NotFound?aspxerrorpath=/Home/non-existent-action

Ces deux techniques proviennent de http://www.niksmit.com/wp/?p=17 (Comment obtenir des pages d'erreur 404 normales (page non trouvée) à l'aide de ASP.Net MVC), pointé à partir de http://richarddingwall.name/2008/08/17/strategies-for-resource-based-404-errors-in-aspnet-mvc/

Si, dans Web.config, j'ai <customErrors mode="On" defaultRedirect="~/Error/Unknown" redirectMode="ResponseRedirect" />, j'obtiens le code de réponse approprié, mais mon contrôleur d'erreur n'est jamais appelé. Supprimer l'attribut redirectMode me donne 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 ressemble à un 404).

Voici quelques-unes des choses que j'ai lues et que j'ai essayé d'implémenter:

.. avec un tas de messages StackOverflow.

Il me semble que ce type de gestion des erreurs est assez basique pour les applications Web, et que le framework MVC devrait avoir des valeurs par défaut qui permettent de le faire immédiatement et de laisser les gens l’étendre pour fonctionner autrement. Peut-être qu'ils le feront dans une prochaine version. En attendant, quelqu'un peut-il me donner des détails complets sur la manière de mettre en œuvre les réponses HTTP appropriées?

38
Val

Voici une technique que vous pouvez utiliser. Définissez une 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");
    }
}

puis, dans Global.asax, vous pouvez vous abonner à l'événement Application_Error dans lequel vous pouvez enregistrer l'exception et exécuter l'action correspondante de la 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, il ne reste plus qu'à lancer les exceptions appropriées:

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

Pour les erreurs HTTP 404 (sans redirections), jetez un œil à mon article de blog sur le sujet. Cela pourrait vous donner de bonnes idées:

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

4
Hector Correa

C'est une très vieille question. mais je pensais que cela valait la peine si je vous présentais une façon beaucoup plus propre de gérer les exceptions HTTP que j'ai vues dans cher "La réponse de Jesse Webb" .

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

<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

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

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

Note : Ceci ne fonctionne que dans IIS 7 et plus récent. (En raison de l’élément httpErrors qui a été ajouté récemment.

0

Cela ne répond pas à votre question, mais il est important de noter que le statut HTTP 500 indique que quelque chose s'est mal passé sur le serveur. Votre exemple:

POST /User/New/new-user-name-too-long-for-db-column-constraint

N’est pas un motif valable pour lancer un 500, c’est un problème de validation des données et doit être traité par des annotations de données MVC ou un cadre de validation jQuery, etc.

0
JK.