web-dev-qa-db-fra.com

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

J'utilise RC2

tilisation du routage d'URL:

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

Ce qui précède semble prendre en charge les requêtes comme celle-ci (en supposant que les tables de routage par défaut soient configurées par le projet MVC initial): "/ blah/blah/blah/blah"

Remplacement de 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);
}  

Toutefois, les stratégies précédentes ne gèrent pas une demande adressée à un contrôleur incorrect/inconnu. Par exemple, je n'ai pas de "/ IDoNotExist", si je le demande, je reçois la page générique 404 du serveur Web et non mon 404 si j'utilise le routage + le remplacement.

Donc enfin, ma question est la suivante: Y at-il un moyen d’attraper ce type de demande en utilisant un itinéraire ou quelque chose d’autre dans le framework MVC lui-même?

OU devrais-je simplement utiliser Web.Config customErrors en tant que gestionnaire 404 et oublier tout cela? Je suppose que si j'utilise customErrors, je devrai stocker la page générique 404 en dehors de/Views en raison des restrictions Web.Config relatives à l'accès direct.

424
Brian

Le code provient de http://blogs.Microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2 .aspx et fonctionne également dans ASP.net MVC 1.0

Voici comment je gère les exceptions http:

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));
}
263
Shay Jacoby

Conditions requises pour 404

Ce qui suit sont mes exigences pour une solution 404 et ci-dessous, je montre comment je l’implémente:

  • Je veux gérer des itinéraires correspondants avec de mauvaises actions
  • Je veux gérer des itinéraires correspondants avec de mauvais contrôleurs
  • Je souhaite gérer des itinéraires sans correspondance (des URL arbitraires que mon application ne peut pas comprendre). Je ne veux pas que ces informations remontent au Global.asax ou à IIS, car alors je peux t rediriger correctement dans mon application MVC
  • Je veux un moyen de gérer de la même manière que ci-dessus, les 404 personnalisés - comme lorsqu'un ID est soumis pour un objet qui n'existe pas (peut-être supprimé)
  • Je veux que tous mes 404 renvoient une vue MVC (et non une page statique) sur laquelle je peux pomper plus de données plus tard si nécessaire ( bonnes conceptions 404 ) et ils doivent renvoyer le code d'état HTTP 404

Solution

Je pense que vous devriez enregistrer Application_Error dans Global.asax pour des raisons plus élevées, telles que les exceptions non gérées et la journalisation (comme la réponse de Shay Jacoby ), mais pas la gestion. C'est pourquoi ma suggestion empêche les éléments 404 de figurer dans le fichier Global.asax.

Étape 1: avoir un lieu commun pour la logique d'erreur 404

C'est une bonne idée pour la maintenabilité. Utilisez un ErrorController pour que les améliorations futures de votre page 404 bien conçue puissent s’adapter facilement. En outre, assurez-vous que votre réponse contient 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: Utilisez une classe de contrôleur de base pour pouvoir facilement appeler votre action 404 personnalisée et câbler le fil HandleUnknownAction

404 dans ASP.NET MVC doivent être capturés à plusieurs endroits. Le premier est HandleUnknownAction.

La méthode InvokeHttp404 crée un emplacement commun pour le réacheminement vers l'action ErrorController et notre nouvelle action Http404. 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: Utilisez l’injection de dépendance dans votre Controller Factory et connectez 404 HttpExceptions

Comme si (il n'est pas nécessaire que ce soit StructureMap):

exemple MVC1.0:

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

exemple MVC2.0:

    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 qu'il est préférable de détecter les erreurs plus près de leur origine. C'est pourquoi je préfère ce qui précède au gestionnaire Application_Error.

Ceci est la deuxième place pour attraper 404.

Étape 4: ajoutez un itinéraire NotFound à Global.asax pour les URL qui ne sont pas analysées dans votre application.

Cet itinéraire devrait pointer sur notre action Http404. Notez que le paramètre url sera une URL relative car le moteur de routage supprime la partie de domaine ici? C’est pourquoi nous avons toute cette logique d’URL conditionnelle à l’étape 1.

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

Ceci est le troisième et dernier endroit pour attraper 404 dans une application MVC que vous ne vous invoquez pas. Si vous ne trouvez pas d'itinéraires sans correspondance ici, MVC transmettra le problème à ASP.NET (Global.asax) et vous ne le voudrez pas vraiment dans cette situation.

Étape 5: Enfin, appelez 404 lorsque votre application ne trouve rien

Comme quand un mauvais identifiant est soumis à mon contrôleur de prêts (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 connecté dans moins d'endroits avec moins de code, mais je pense que cette solution est plus facile à maintenir, plus testable et assez pragmatique.

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

NOTE: Ceci a été modifié de manière significative par rapport à ma réponse d'origine mais le but/les exigences sont les mêmes - c'est pourquoi je n'ai pas ajouté de nouvelle réponse

252
Matt Kocaj

ASP.NET MVC ne prend pas très bien en charge les pages 404 personnalisées. Usine de contrôleur personnalisé, route à tout faire, classe de contrôleur de base avec HandleUnknownAction - argh!

Les pages d'erreur personnalisées IIS sont une meilleure alternative jusqu'à présent:

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

232
Pavel Chuchuva

Réponse rapide/TL; DR

enter image description here

Pour les paresseux:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

Puis supprimez cette ligne de global.asax

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

Et ceci ne concerne que IIS7 + et IIS Express.

Si vous utilisez Cassini… eh bien… euh… euh… maladroit… awkward


Longue réponse expliquée

Je sais que cela a été répondu. Mais la réponse est VRAIMENT SIMPLE (acclamations David Fowler et Damian Edwards pour avoir vraiment répondu à cette question).

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

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

Étape 1 -> Mettez à jour votre web.config en DEUX places.

<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 bien note des ITINÉRAIRES que j'ai décidé d'utiliser. Vous pouvez utiliser n'importe quoi, mais mes routes sont

  • /NotFound <- pour une page d'erreur 404 non trouvée.
  • /ServerError <- pour toute autre erreur, incluez les erreurs qui se produisent dans mon code. c'est une erreur de 500 serveur interne

Voyez comment la première section de <system.web> ne contient que une entrée personnalisée? L'entrée statusCode="404"? J'ai répertorié un seul code d'état car toutes les autres erreurs, y compris le 500 Server Error (c'est-à-dire les erreurs embêtantes qui se produisent lorsque votre code a un bogue et bloque la demande de l'utilisateur) .. toutes les autres erreurs sont gérées par le paramètre defaultRedirect="/ServerError" .. qui indique que, si vous n'êtes pas une page 404 introuvable, veuillez vous rendre à la route /ServerError.

D'accord. c'est à l'écart .. maintenant pour mes routes listées dans global.asax

Étape 2 - Création des itinéraires dans Global.asax

Voici ma section complète de route ..

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}
        );
}

Cela répertorie deux routes ignorées -> axd's et favicons (bonus de route ignorée, pour vous!) Ensuite (et la commande est IMPÉRATIVE ICI), j'ai mes deux routes explicites de gestion des erreurs .. suivies par d'autres voies. Dans ce cas, celui par défaut. Bien sûr, j'en ai plus, mais c'est spécial pour mon site web. Assurez-vous simplement que les routes d'erreur sont en haut de la liste. L'ordre est impératif .

Enfin, alors que nous sommes dans notre fichier global.asax, nous n'enregistrons PAS globalement l'attribut HandleError. Non, non, non monsieur. Nadda. Nan. Nien. Négatif. Noooooooooo ...

Supprimer cette ligne de global.asax

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

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

Maintenant .. nous ajoutons un contrôleur avec 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 ceci. Tout d'abord, il n'y a PAS[HandleError] attribut ici. Pourquoi? Parce que le framework ASP.NET intégré gère déjà les 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 là-bas. Si vous souhaitez afficher des informations sur une exception, vous pouvez utiliser Server.GetLastError() pour obtenir ces informations.

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

Étape 4 - Créer les vues

Et enfin, créez deux vues. Mettez-les dans le point de vue normal, pour ce contrôleur.

enter image description here

Commentaires bonus

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

Et ça, mes amis, ça devrait être ça.

Maintenant, félicitations pour avoir autant lu et méritez une Licorne!

enter image description here

153
Pure.Krome

J'ai étudié BEAUCOUP sur la façon de gérer correctement les 404 dans MVC (plus précisément MVC3) , et Ceci, IMHO est la meilleure solution que je suis venu avec:

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

(facultatif)

Explication:

Autant que je sache, une application ASP.NET MVC3 peut générer 404 cas différents.

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

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

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

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

(3) Une URL trouve une correspondance dans la table de routage, mais spécifie une action inexistante.

(généré manuellement:)

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

(5) Une action lève une exception HttpException avec le code d'état 404.

(6) Une action modifie manuellement la propriété Response.StatusCode sur 404.

Normalement, vous voulez accomplir 3 objectifs:

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

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

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

Il y a différentes façons d'essayer d'accomplir ceci:

(1)

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

Problèmes avec cette solution:

  1. Ne répond pas à l'objectif (1) dans les cas (1), (4), (6).
  2. Ne respecte pas l'objectif (2) automatiquement. Il doit être programmé manuellement.
  3. Non 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>

Problèmes avec cette solution:

  1. Ne fonctionne que sur IIS 7+.
  2. Ne répond pas à l'objectif (1) dans les cas (2), (3), (5).
  3. Ne respecte pas l'objectif (2) automatiquement. Il doit être programmé manuellement.

(3)

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

  1. Ne fonctionne que sur IIS 7+.
  2. Ne respecte pas l'objectif (2) automatiquement. Il doit être programmé manuellement.
  3. 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.

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

Problèmes avec cette solution:

  1. Ne fonctionne que sur IIS 7+.
  2. Ne respecte pas l'objectif (2) automatiquement. Il doit être programmé manuellement.
  3. Ne correspond pas à l'objectif (3) dans les cas (2), (3), (5).

Les personnes qui en avaient déjà été victimes avaient 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é d'utiliser une bibliothèque externe.

85
Marco

J'aime beaucoup la solution de cottsaks et pense que c'est très clairement expliqué. mon seul ajout était de modifier l'étape 2 comme suit

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
}

En gros, cela empêche les URL contenant des actions non valides ET les contrôleurs de déclencher deux fois la routine d'exception. par exemple pour les URL telles que asdfsdf/dfgdfgd

13
Dave Lowe

La seule façon pour moi de faire en sorte que la méthode de @ cottsak fonctionne avec des contrôleurs non valides était de modifier la demande de routage existante dans CustomControllerFactory, comme suit:

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType); 
            else
                return ObjectFactory.GetInstance(controllerType) as Controller;
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                requestContext.RouteData.Values["controller"] = "Error";
                requestContext.RouteData.Values["action"] = "Http404";
                requestContext.RouteData.Values.Add("url", requestContext.HttpContext.Request.Url.OriginalString);

                return ObjectFactory.GetInstance<ErrorController>();
            }
            else
                throw ex;
        }
    }
}

Je devrais mentionner que j'utilise MVC 2.0.

6
Dave K

Voici une autre méthode utilisant les outils MVC qui vous permet de gérer les demandes adressées à des noms de contrôleur, à des noms de route, et à tout autre critère que vous voyez s’intégrer à une méthode Action. Personnellement, je préfère éviter autant de paramètres web.config que possible, car ils effectuent la redirection 302/200 et ne prennent pas en charge ResponseRewrite (Server.Transfer) à l'aide de vues Razor. Je préférerais renvoyer un 404 avec une page d'erreur personnalisée pour des raisons de référencement.

Une partie de ceci est une nouvelle prise de la technique de Cottsak ci-dessus.

Cette solution utilise également des paramètres web.config minimaux, privilégiant les filtres d’erreur MVC 3.

Usage

Lancez simplement une exception HttpException d'une action ou d'un ActionFilterAttribute personnalisé.

Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")

Étape 1

Ajoutez le paramètre suivant à votre web.config. Ceci est nécessaire pour utiliser le HandleErrorAttribute de MVC.

<customErrors mode="On" redirectMode="ResponseRedirect" />

Étape 2

Ajoutez un HandleHttpErrorAttribute personnalisé similaire au HandleErrorAttribute du framework MVC, à l'exception des erreurs HTTP:

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"

    Private m_HttpCode As HttpStatusCode
    Private m_Master As String
    Private m_View As String

    Public Property HttpCode As HttpStatusCode
        Get
            If m_HttpCode = 0 Then
                Return HttpStatusCode.NotFound
            End If
            Return m_HttpCode
        End Get
        Set(value As HttpStatusCode)
            m_HttpCode = value
        End Set
    End Property

    Public Property Master As String
        Get
            Return If(m_Master, String.Empty)
        End Get
        Set(value As String)
            m_Master = value
        End Set
    End Property

    Public Property View As String
        Get
            If String.IsNullOrEmpty(m_View) Then
                Return String.Format(m_DefaultViewFormat, Me.HttpCode)
            End If
            Return m_View
        End Get
        Set(value As String)
            m_View = value
        End Set
    End Property

    Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
        If filterContext Is Nothing Then Throw New ArgumentException("filterContext")

        If filterContext.IsChildAction Then
            Return
        End If

        If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
            Return
        End If

        Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
        If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
            Return
        End If

        If ex.GetHttpCode <> Me.HttpCode Then
            Return
        End If

        Dim controllerName As String = filterContext.RouteData.Values("controller")
        Dim actionName As String = filterContext.RouteData.Values("action")
        Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)

        filterContext.Result = New ViewResult With {
            .ViewName = Me.View,
            .MasterName = Me.Master,
            .ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
            .TempData = filterContext.Controller.TempData
        }
        filterContext.ExceptionHandled = True
        filterContext.HttpContext.Response.Clear()
        filterContext.HttpContext.Response.StatusCode = Me.HttpCode
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
    End Sub
End Class

Étape 3

Ajoutez des filtres à la GlobalFilterCollection (GlobalFilters.Filters) dans Global.asax. Cet exemple acheminera toutes les erreurs InternalServerError (500) vers la vue d'erreur partagée (Views/Shared/Error.vbhtml). Les erreurs NotFound (404) seront également envoyées à ErrorHttp404.vbhtml dans les vues partagées. J'ai ajouté une erreur 401 ici pour vous montrer comment cela peut être étendu pour des codes d'erreur HTTP supplémentaires. Notez que ces vues doivent être partagées et qu'elles utilisent toutes l'objet System.Web.Mvc.HandleErrorInfo en tant que modèle.

filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})

Étape 4

Créez une classe de contrôleur de base et en héritez dans vos contrôleurs. Cette étape nous permet de gérer des noms d’action inconnus et de générer l’erreur HTTP 404 dans notre HandleHttpErrorAttribute.

Public Class BaseController
    Inherits System.Web.Mvc.Controller

    Protected Overrides Sub HandleUnknownAction(actionName As String)
        Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
    End Sub

    Public Function Unknown() As ActionResult
        Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
        Return New EmptyResult
    End Function
End Class

Étape 5

Créez une substitution ControllerFactory et remplacez-la dans votre fichier Global.asax dans Application_Start. Cette étape nous permet de déclencher l'exception HTTP 404 lorsqu'un nom de contrôleur non valide a été spécifié.

Public Class MyControllerFactory
    Inherits DefaultControllerFactory

    Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
        Try
            Return MyBase.GetControllerInstance(requestContext, controllerType)
        Catch ex As HttpException
            Return DependencyResolver.Current.GetService(Of BaseController)()
        End Try
    End Function
End Class

'In Global.asax.vb Application_Start:

controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)

Étape 6

Incluez un itinéraire spécial dans votre RoutTable.Routes pour l'action BaseController Unknown. Cela nous aidera à lever un 404 dans le cas où un utilisateur accède à un contrôleur inconnu ou à une action inconnue.

'BaseController
routes.MapRoute( _
    "Unknown", "BaseController/{action}/{id}", _
    New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)

Sommaire

Cet exemple montre comment utiliser la structure MVC pour renvoyer 404 codes d'erreur HTTP au navigateur sans redirection, à l'aide d'attributs de filtre et de vues d'erreur partagées. Il montre également l'affichage de la même page d'erreur personnalisée lorsque des noms de contrôleur et des noms d'action non valides sont spécifiés.

J'ajouterai une capture d'écran d'un nom de contrôleur non valide, d'un nom d'action et d'un 404 personnalisé généré à partir de l'action Home/TriggerNotFound si je reçois suffisamment de votes pour en envoyer un =). Fiddler renvoie un message 404 lorsque j'accède aux URL suivantes à l'aide de cette solution:

/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound

le post de cottsak ci-dessus et ces articles étaient de bonnes références.

4
sky-dev

Ma solution raccourcie qui fonctionne avec les zones non gérées, les contrôleurs et les actions:

  1. Créez une vue 404.cshtml.

  2. Créez une classe de base pour vos contrôleurs:

    public class Controller : System.Web.Mvc.Controller
    {
        protected override void HandleUnknownAction(string actionName)
        {
            Http404().ExecuteResult(ControllerContext);
        }
    
        protected virtual ViewResult Http404()
        {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return View("404");
        }
    }
    
  3. Créez une usine de contrôleur personnalisée renvoyant votre contrôleur de base en tant que solution de repli:

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
    
  4. Ajoutez à Application_Start() la ligne suivante:

    ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
    
4
Herman Kan

Dans MVC4, WebAPI 404 peut être géré de la manière suivante:

COURS APICONTROLLER

    // GET /api/courses/5
    public HttpResponseMessage<Courses> Get(int id)
    {
        HttpResponseMessage<Courses> resp = null;

        var aCourse = _courses.Where(c => c.Id == id).FirstOrDefault();

        resp = aCourse == null ? new HttpResponseMessage<Courses>(System.Net.HttpStatusCode.NotFound) : new HttpResponseMessage<Courses>(aCourse);

        return resp;
    }

CONTRÔLEUR DOMESTIQUE

public ActionResult Course(int id)
{
    return View(id);
}

VUE

<div id="course"></div>
<script type="text/javascript">
    var id = @Model;
    var course = $('#course');
    $.ajax({    
        url: '/api/courses/' + id,
        success: function (data) {
            course.text(data.Name);
        },
        statusCode: {
            404: function() 
            {
                course.text('Course not available!');    
            }
        }
    });
</script>

GLOBAL

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

RÉSULTATS

enter image description here

3
Diganta Kumar

Essayez NotFoundMVC sur un nuget. Cela fonctionne, pas de configuration.

2
DarthVader

Il me semble que la configuration standard de CustomErrors devrait fonctionner cependant, en raison de la confiance accordée à Server.Transfer, il semble que le L'implémentation interne de ResponseRewrite n'est pas compatible avec MVC.

Cela ressemble à un trou de fonctionnalité criant pour moi, alors j'ai décidé de ré-implémenter cette fonctionnalité en utilisant un module HTTP. La solution ci-dessous vous permet de gérer n’importe quel code de statut HTTP (y compris le code 404) en le redirigeant vers n’importe quel itinéraire MVC valide, comme vous le feriez normalement.

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="404.aspx" />
    <error statusCode="500" redirect="~/MVCErrorPage" />
</customErrors>

Cela a été testé sur les plates-formes suivantes;

  • MVC4 en mode pipeline intégré (IIS Express 8)
  • MVC4 en mode classique (VS Development Server, Cassini)
  • MVC4 en mode classique (IIS6)

Avantages

  • Solution générique pouvant être insérée dans n'importe quel projet MVC
  • Active la prise en charge de la configuration des erreurs personnalisées traditionnelles
  • Fonctionne dans les modes Pipeline intégré et Classique

La solution

namespace Foo.Bar.Modules {

    /// <summary>
    /// Enables support for CustomErrors ResponseRewrite mode in MVC.
    /// </summary>
    public class ErrorHandler : IHttpModule {

        private HttpContext HttpContext { get { return HttpContext.Current; } }
        private CustomErrorsSection CustomErrors { get; set; }

        public void Init(HttpApplication application) {
            System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
            CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");

            application.EndRequest += Application_EndRequest;
        }

        protected void Application_EndRequest(object sender, EventArgs e) {

            // only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
            if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {

                int statusCode = HttpContext.Response.StatusCode;

                // if this request has thrown an exception then find the real status code
                Exception exception = HttpContext.Error;
                if (exception != null) {
                    // set default error status code for application exceptions
                    statusCode = (int)HttpStatusCode.InternalServerError;
                }

                HttpException httpException = exception as HttpException;
                if (httpException != null) {
                    statusCode = httpException.GetHttpCode();
                }

                if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {

                    Dictionary<int, string> errorPaths = new Dictionary<int, string>();

                    foreach (CustomError error in CustomErrors.Errors) {
                        errorPaths.Add(error.StatusCode, error.Redirect);
                    }

                    // find a custom error path for this status code
                    if (errorPaths.Keys.Contains(statusCode)) {
                        string url = errorPaths[statusCode];

                        // avoid circular redirects
                        if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {

                            HttpContext.Response.Clear();
                            HttpContext.Response.TrySkipIisCustomErrors = true;

                            HttpContext.Server.ClearError();

                            // do the redirect here
                            if (HttpRuntime.UsingIntegratedPipeline) {
                                HttpContext.Server.TransferRequest(url, true);
                            }
                            else {
                                HttpContext.RewritePath(url, false);

                                IHttpHandler httpHandler = new MvcHttpHandler();
                                httpHandler.ProcessRequest(HttpContext);
                            }

                            // return the original status code to the client
                            // (this won't work in integrated pipleline mode)
                            HttpContext.Response.StatusCode = statusCode;

                        }
                    }

                }

            }

        }

        public void Dispose() {

        }


    }

}

Utilisation

Incluez ceci comme module HTTP final dans votre web.config

  <system.web>
    <httpModules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </httpModules>
  </system.web>

  <!-- IIS7+ -->
  <system.webServer>
    <modules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </modules>
  </system.webServer>

Pour ceux qui y prêtent attention, vous remarquerez qu'en mode Pipeline intégré, cela répondra toujours avec HTTP 200 en raison du fonctionnement de Server.TransferRequest. Pour retourner le code d'erreur approprié, j'utilise le contrôleur d'erreur suivant.

public class ErrorController : Controller {

    public ErrorController() { }

    public ActionResult Index(int id) {
        // pass real error code to client
        HttpContext.Response.StatusCode = id;
        HttpContext.Response.TrySkipIisCustomErrors = true;

        return View("Errors/" + id.ToString());
    }

}
2
Red Taz

Ma solution, au cas où quelqu'un la trouverait utile.

Dans Web.config:

<system.web>
    <customErrors mode="On" defaultRedirect="Error" >
      <error statusCode="404" redirect="~/Error/PageNotFound"/>
    </customErrors>
    ...
</system.web>

Dans Controllers/ErrorController.cs:

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        if(Request.IsAjaxRequest()) {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return Content("Not Found", "text/plain");
        }

        return View();
    }
}

Ajoutez un PageNotFound.cshtml dans le dossier Shared, et c'est tout.

2
Konamiman

Traiter les erreurs dans ASP.NET MVC n'est qu'un problème. J'ai essayé beaucoup de suggestions sur cette page et sur d'autres questions et sites et rien ne marche bien. Une suggestion était de gérer les erreurs sur web.config à l'intérieur system.webserver mais cela juste retourne des pages blanches.

Mon objectif en proposant cette solution était de:

  • PAS REDIRECT
  • Retournez les bons codes d'état 200/Ok comme le traitement d'erreur par défaut

Voici ma solution.

1. Ajoutez ce qui suit à la section system.web

   <system.web>
     <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404"  redirect="~/Error/404.aspx" />
      <error statusCode="500" redirect="~/Error/500.aspx" />
     </customErrors>
    <system.web>

Ce qui précède gère toutes les URL non gérées par routes.config et les exceptions non gérées, en particulier celles rencontrées dans les vues. Notez que j'ai utilisé aspx pas html . C'est pour que je puisse ajouter un code de réponse sur le code qui se trouve derrière.

2. Créez un dossier appelé Erreur (ou ce que vous préférez) à la racine de votre projet et ajoutez les deux formulaires Web. Ci-dessous ma page 404;

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="404.aspx.cs" Inherits="Myapp.Error._404" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title >Page Not found</title>
    <link href="<%=ResolveUrl("~/Content/myapp.css")%>" rel="stylesheet" />
</head>
<body>
    <div class="top-nav">
      <a runat="server" class="company-logo" href="~/"></a>
    </div>
    <div>
        <h1>404 - Page Not found</h1>
        <p>The page you are looking for cannot be found.</p>
        <hr />
        <footer></footer>
    </div>
</body>
</html>

Et sur le code derrière je mets le code de réponse

protected void Page_Load(object sender, EventArgs e)
{
    Response.StatusCode = 404;
}

Faites la même chose pour la page 500

Pour gérer les erreurs dans les contrôleurs. Il y a plusieurs façons de le faire. C'est ce qui a fonctionné pour moi. Tous mes contrôleurs héritent d'un contrôleur de base. Dans le contrôleur de base, j'ai les méthodes suivantes

protected ActionResult ShowNotFound()
{
    return ShowNotFound("Page not found....");
}

protected ActionResult ShowNotFound(string message)
{
    return ShowCustomError(HttpStatusCode.NotFound, message);
}

protected ActionResult ShowServerError()
{
    return ShowServerError("Application error....");
}

protected ActionResult ShowServerError(string message)
{
    return ShowCustomError(HttpStatusCode.InternalServerError, message);
}

protected ActionResult ShowNotAuthorized()
{
    return ShowNotAuthorized("You are not allowed ....");

}

protected ActionResult ShowNotAuthorized(string message)
{
    return ShowCustomError(HttpStatusCode.Forbidden, message);
}

protected ActionResult ShowCustomError(HttpStatusCode statusCode, string message)
{
    Response.StatusCode = (int)statusCode;
    string title = "";
    switch (statusCode)
    {
        case HttpStatusCode.NotFound:
            title = "404 - Not found";
            break;
        case HttpStatusCode.Forbidden:
            title = "403 - Access Denied";
            break;
        default:
            title = "500 - Application Error";
            break;
    }
    ViewBag.Title = title;
    ViewBag.Message = message;
    return View("CustomError");
}

4. Ajoutez le CustomError.cshtml à votre dossier de vues partagé . Ci-dessous est le mien;

<h1>@ViewBag.Title</h1>
<br />
<p>@ViewBag.Message</p>

Maintenant, dans votre contrôleur d’application, vous pouvez faire quelque chose comme ceci;

public class WidgetsController : ControllerBase
{
  [HttpGet]
  public ActionResult Edit(int id)
  {
    Try
    {
       var widget = db.getWidgetById(id);
       if(widget == null)
          return ShowNotFound();
          //or return ShowNotFound("Invalid widget!");
       return View(widget);
    }
    catch(Exception ex)
    {
       //log error
       logger.Error(ex)
       return ShowServerError();
    }
  }
}

Maintenant pour le caveat. Il ne gérera pas les erreurs de fichiers statiques. Donc, si vous avez un itinéraire tel que example.com/widgets et que l'utilisateur le modifie en example.com/ widgets.html , ils obtiendront la page d'erreur IIS par défaut, vous devrez donc gérer les erreurs de niveau IIS d'une autre manière.

2
Moses Machua

Poster une réponse car mon commentaire était trop long ...

C'est à la fois un commentaire et des questions sur le message/réponse de la Licorne:

https://stackoverflow.com/a/7499406/687549

Je préfère cette réponse aux autres pour sa simplicité et le fait qu'apparemment, certaines personnes de Microsoft ont été consultées. J'ai trois questions cependant et si on peut y répondre, j'appellerai cette réponse le Saint Graal de toutes les réponses d'erreur 404/500 sur les interwebs d'une application ASP.NET MVC (x).

@ Pure.Krome

  1. Pouvez-vous mettre à jour votre réponse avec les éléments de référencement des commentaires signalés par GWB (il n’a jamais été fait mention de cela dans votre réponse) - <customErrors mode="On" redirectMode="ResponseRewrite"> et <httpErrors errorMode="Custom" existingResponse="Replace">?

  2. Pouvez-vous demander à vos amis de l’équipe ASP.NET s’il est acceptable de le faire ainsi - ce serait bien d’avoir une confirmation - c’est peut-être un grand non-non pour changer redirectMode et existingResponse de cette façon être capable de bien jouer avec le référencement?!

  3. Pouvez-vous ajouter des précisions concernant tout cela (customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", SUPPRIMER customErrors COMPLÈTEMENT comme l'a suggéré quelqu'un) après avoir parlé à vos amis chez Microsoft?

Comme je le disais; ce serait un surcroît si nous pouvions rendre votre réponse plus complète car cela semble être une question assez populaire avec plus de 54 000 vues.

Mise à jour : La réponse de la Licorne effectue un résultat 302 trouvé et un résultat correct 200 et ne peut pas être modifiée pour ne renvoyer que 404 via un itinéraire. Ce doit être un fichier physique qui n'est pas très MVC: ish. Passons donc à une autre solution. Dommage car cela semblait être l'ultime MVC: ish réponds jusqu'à présent.

1
PussInBoots

Ajout de ma solution, qui est presque identique à celle de Herman Kan, avec une petite ride pour lui permettre de fonctionner pour mon projet.

Créez un contrôleur d'erreur personnalisé:

public class Error404Controller : BaseController
{
    [HttpGet]
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View("404");
    }
}

Créez ensuite une fabrique de contrôleurs personnalisés:

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        return controllerType == null ? new Error404Controller() : base.GetControllerInstance(requestContext, controllerType);
    }
}

Enfin, ajoutez un remplacement au contrôleur d'erreur personnalisé:

protected override void HandleUnknownAction(string actionName)
{
    var errorRoute = new RouteData();
    errorRoute.Values.Add("controller", "Error404");
    errorRoute.Values.Add("action", "PageNotFound");
    new Error404Controller().Execute(new RequestContext(HttpContext, errorRoute));
}

Et c'est tout. Pas besoin de modifications de Web.config.

1
Rob Lyndon

1) Créez une classe de contrôleur abstraite.

public abstract class MyController:Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;
        return View("NotFound");
    }

    protected override void HandleUnknownAction(string actionName)
    {
        this.ActionInvoker.InvokeAction(this.ControllerContext, "NotFound");
    }
    protected override void OnAuthorization(AuthorizationContext filterContext) { }
}  

2) Créez l'héritage de cette classe abstraite dans tous vos contrôleurs

public class HomeController : MyController
{}  

3) Et ajoutez une vue nommée "NotFound" dans votre dossier View-Shared.

1
Mehmet

J'ai parcouru la plupart des solutions affichées sur ce fil. Même si cette question est peut-être ancienne, elle est encore très applicable aux nouveaux projets, aussi ai-je passé beaucoup de temps à lire les réponses présentées ici ainsi que ailleurs.

Comme @Marco a souligné les différents cas dans lesquels une 404 peut se produire, j'ai vérifié la solution que j'ai compilée ensemble sur cette liste. En plus de sa liste d'exigences, j'ai également ajouté une autre.

  • La solution doit pouvoir gérer les appels MVC ainsi que les appels AJAX/WebAPI de la manière la plus appropriée. (Par exemple, si 404 se produit dans MVC, la page Introuvable doit s'afficher et si 404 se présente dans WebAPI, il ne faut pas détourner la réponse XML/JSON afin que le Javascript qui consomme puisse l'analyser facilement).

Cette solution est 2 fois:

La première partie provient de @Guillaume à l’adresse https://stackoverflow.com/a/27354140/2310818 . Leur solution prend en charge tous les 404 causés par une route, un contrôleur et une action non valides.

L'idée est de créer un formulaire Web, puis de le faire appeler l'action NotFound de votre contrôleur d'erreurs MVC. Tout cela sans aucune redirection, vous ne verrez donc pas un seul 302 dans Fiddler. L'URL d'origine est également préservée, ce qui rend cette solution fantastique!


La seconde partie provient de @ Germán at https://stackoverflow.com/a/5536676/2310818 . Leur solution prend en charge tous les 404 retournés par vos actions sous la forme de HttpNotFoundResult () ou jette une nouvelle HttpException ()!

L'idée est de faire en sorte que le filtre examine la réponse ainsi que l'exception émise par vos contrôleurs MVC et appelle l'action appropriée dans votre contrôleur d'erreurs. Encore une fois, cette solution fonctionne sans aucune redirection et l'URL d'origine est conservée!


Comme vous pouvez le constater, ces deux solutions combinées offrent un mécanisme de traitement des erreurs très robuste et répondent à toutes les exigences répertoriées par @Marco, ainsi qu'à mes exigences. Si vous souhaitez voir un échantillon de travail ou une démonstration de cette solution, veuillez laisser les commentaires et je serais heureux de les assembler.

0
Parth Shah

J'ai parcouru tous les articles, mais rien ne fonctionne pour moi: mon type d'utilisateur requis, dans votre page 404 personnalisée, doit afficher une URL personnalisée.Je pensais que c'était très simple.

 <system.web>
    <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404" redirect="~/PageNotFound.aspx"/>
    </customErrors>
  </system.web>
<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404"/>
      <error statusCode="404" path="/PageNotFound.html" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

J'ai trouvé cet article très utile. Il devrait être lu immédiatement . Custome error page-Ben Foster

0
Thakur Rock