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

J’utilise RC2

Utiliser le 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 ont été configurées par le projet MVC initial): “/ blah / blah / blah / blah”

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

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

Cependant, les stratégies précédentes ne traitent pas une requête adressée à un contrôleur Bad / Unknown. Par exemple, je n’ai pas de “/ IDoNotExist”, si je demande ceci, je reçois la page 404 générique du serveur Web et non la page 404 si j’utilise le routage + la substitution.

Donc, enfin, ma question est la suivante: existe-t-il un moyen d’attraper ce type de requête en utilisant une route ou autre chose dans le framework MVC lui-même?

OU devrais-je simplement utiliser Web.Config customErrors comme gestionnaire 404 et oublier tout cela? Je suppose que si je vais avec customErrors, je vais devoir stocker la page 404 générique en dehors de / Views en raison des ressortingctions de Web.Config sur l’access direct.

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 dans ASP.net MVC 1.0 aussi

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

Conditions requirejses pour 404

Voici mes besoins pour une solution 404 et ci-dessous, je montre comment je l’implémente:

  • Je veux gérer des routes correspondantes avec de mauvaises actions
  • Je veux gérer des itinéraires correspondants avec de mauvais contrôleurs
  • Je veux gérer des routes non appariées (des URL arbitraires que mon application ne peut pas comprendre) – je ne veux pas que celles-ci débordent sur Global.asax ou IIS car je ne peux pas redirect correctement vers 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 identifiant est soumis pour un object qui n’existe pas (peut-être supprimé)
  • Je souhaite que tous mes 404s renvoient une vue MVC (pas une page statique) vers laquelle je pourrai pomper plus de données ultérieurement si nécessaire ( bonnes conceptions 404 ) et elles doivent renvoyer le code d’état HTTP 404

Solution

Je pense que vous devriez enregistrer Application_Error dans le fichier Global.asax pour des choses plus complexes, comme les exceptions et la journalisation non gérées (comme les réponses aux questions de Shay Jacoby ), mais pas la gestion 404. C’est pourquoi ma suggestion garde les éléments 404 du 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 futures améliorations de votre page 404 bien conçue puissent s’adapter facilement. Assurez-vous également que votre réponse porte le code 404 !

 public class ErrorController : MyController { #region Http404 public ActionResult Http404(ssortingng 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.OriginalSsortingng.Contains(url) & Request.Url.OriginalSsortingng != url ? Request.Url.OriginalSsortingng : 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.OriginalSsortingng != model.RequestedUrl ? Request.UrlReferrer.OriginalSsortingng : null; // TODO: insert ILogger here return View("NotFound", model); } public class NotFoundViewModel { public ssortingng RequestedUrl { get; set; } public ssortingng ReferrerUrl { get; set; } } #endregion } 

Étape 2: utilisez une classe de contrôleur de base pour pouvoir facilement invoquer votre action 404 personnalisée et câbler HandleUnknownAction

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

La méthode InvokeHttp404 crée un emplacement commun pour le réacheminement vers ErrorController et notre nouvelle action Http404 . Pensez SEC !

 public abstract class MyController : Controller { #region Http404 handling protected override void HandleUnknownAction(ssortingng 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(); var errorRoute = new RouteData(); errorRoute.Values.Add("controller", "Error"); errorRoute.Values.Add("action", "Http404"); errorRoute.Values.Add("url", httpContext.Request.Url.OriginalSsortingng); errorController.Execute(new RequestContext( httpContext, errorRoute)); return new EmptyResult(); } #endregion } 

Étape 3: utilisez l’dependency injection dans votre Controller Factory et connectez les 404 HttpExceptions

Comme ça (ce n’est pas forcément 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).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).InvokeHttp404(requestContext.HttpContext); return errorController; } else throw ex; } return ObjectFactory.GetInstance(controllerType) as Controller; } 

Je pense qu’il vaut mieux attraper les erreurs plus près de leur origine. C’est pourquoi je préfère ce qui précède au gestionnaire Application_Error .

C’est la deuxième place pour attraper 404s.

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

Cette route devrait pointer vers notre action Http404 . Notez que le paramètre url sera une URL relative car le moteur de routage supprime la partie 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 404s dans une application MVC que vous n’invoquez pas vous-même. Si vous n’attrapez pas de routes sans correspondance ici, MVC transmettra le problème à ASP.NET (Global.asax) et vous ne le voulez pas vraiment dans cette situation.

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

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

Merci pour les commentaires jusqu’à présent. J’aimerais 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

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

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

web.config

       

ErrorController

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

Exemple de projet

  • Test404 sur GitHub
  • Site web en direct

Réponse rapide / TL; DR

entrer la description de l'image ici

Pour les fainéants là-bas:

 Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0 

Ensuite, supprimez cette ligne de global.asax

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

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

Si vous utilisez Cassini … eh bien .. euh … euh … maladroit … gênant


Longue réponse expliquée

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

Il n’y a pas besoin de faire quoi que ce soit de coutume .

Pour ASP.NET MVC3 , tous les éléments sont présents.

Étape 1 -> Mettez à jour votre site web.config à deux endroits.

     

et

        ...  ...  

Maintenant, prenez note des ROUTES que j’ai décidé d’utiliser. Vous pouvez utiliser n’importe quoi, mais mes itinéraires sont

  • /NotFound <- pour une page d'erreur 404 introuvable.
  • /ServerError <- pour toute autre erreur, inclure les erreurs qui se produisent dans mon code. il s'agit d'une erreur de serveur interne 500

Voyez comment la première section de ne comporte qu’une seule entrée personnalisée? L’ statusCode="404" ? Je n’ai répertorié qu’un seul code de statut car toutes les autres erreurs, y compris l’ 500 Server Error (c’est-à-dire cette erreur embêtante qui se produit lorsque votre code a un bogue et bloque la demande de l’utilisateur). defaultRedirect="/ServerError" .. qui dit, si vous n’êtes pas une page 404 introuvable, alors s’il vous plaît aller à la route /ServerError .

D’accord. c’est hors du chemin .. maintenant à mes itinéraires répertoriés dans global.asax

Étape 2 – Création des routes dans Global.asax

Voici mon parcours complet.

 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 itinéraires axd's -> axd's et favicons (ooo! Bonus ignore route, pour vous!) Ensuite (et l’ordre est IMPERATIVE HERE), j’ai mes deux routes explicites de traitement des erreurs .. suivies de toutes les autres routes. 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 itinéraires d’erreur sont en haut de la liste. L’ordre est impératif .

Enfin, alors que nous sums à l’intérieur de notre fichier global.asax , nous n’enregistrons PAS globalement l’atsortingbut HandleError. Non, non, non monsieur. Nadda. Nan. Nien. Négatif. Noooooooooo …

Supprimez cette ligne de global.asax

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

É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, laisse ça vérifier. Tout d'abord, il n'y a pas d' atsortingbut [HandleError] ici. Pourquoi? Parce que le framework ASP.NET intégré gère déjà les erreurs ET nous avons spécifié toute la merde nécessaire pour gérer une erreur 🙂 C'est dans cette méthode!

Ensuite, j'ai les deux méthodes d'action. Rien de dur là-bas. Si vous souhaitez afficher des informations sur les exceptions, vous pouvez utiliser Server.GetLastError() pour obtenir ces informations.

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

Étape 4 - Créez les vues

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

entrer la description de l'image ici

Commentaires bonus

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

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

Maintenant, félicitations pour avoir beaucoup lu et avoir une Licorne comme prix!

entrer la description de l'image ici

J’ai étudié BEAUCOUP sur la façon de gérer correctement les 404 dans MVC (en particulier MVC3) , et ceci, à mon humble avis , est la meilleure solution que je propose :

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

(Optionnel)

Explication:

Dans certains cas, les applications ASP.NET MVC3 peuvent générer 404 cas.

(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 jette une exception HttpException avec le code d’état 404.

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

Normalement, vous voulez atteindre 3 objectives:

(1) Afficher une page d’erreur 404 personnalisée pour l’utilisateur.

(2) Maintenir le code de statut 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 plusieurs façons d’essayer d’accomplir ceci:

(1)

      

Problèmes avec cette solution:

  1. Ne respecte pas l’objective (1) dans les cas (1), (4), (6).
  2. Ne respecte pas l’objective (2) automatiquement. Il doit être programmé manuellement.
  3. Ne respecte pas l’objective (3).

(2)

       

Problèmes avec cette solution:

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

(3)

       

Problèmes avec cette solution:

  1. Ne fonctionne que sur IIS 7+.
  2. Ne respecte pas l’objective (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.HandleErrorAtsortingbute, etc. Il ne peut pas seulement afficher les pages d’erreur génériques.

(4)

      

et

       

Problèmes avec cette solution:

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

Les personnes qui en ont déjà souffert 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 cas sans la complexité de l’utilisation d’une bibliothèque externe.

J’aime beaucoup la solution cottsaks et je pense que son explication est très claire. mon seul ajout était de modifier l’étape 2 comme suit

 public abstract class MyController : Controller { #region Http404 handling protected override void HandleUnknownAction(ssortingng 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(); var errorRoute = new RouteData(); errorRoute.Values.Add("controller", "Error"); errorRoute.Values.Add("action", "Http404"); errorRoute.Values.Add("url", httpContext.Request.Url.OriginalSsortingng); errorController.Execute(new RequestContext( httpContext, errorRoute)); return new EmptyResult(); } #endregion } 

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

La seule façon de faire fonctionner la méthode @ cottsak pour les contrôleurs invalides était de modifier la demande de route existante dans CustomControllerFactory, comme ceci:

 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.OriginalSsortingng); return ObjectFactory.GetInstance(); } else throw ex; } } } 

Je devrais mentionner que j’utilise MVC 2.0.

Voici une autre méthode utilisant les outils MVC que vous pouvez gérer avec des requêtes adressées à des noms de contrôleurs incorrects, des noms d’itinéraires incorrects et tout autre critère qui vous semble approprié dans une méthode Action. Personnellement, je préfère éviter autant de parameters web.config que possible, car ils effectuent la redirection Server.Transfer et ne prennent pas en charge ResponseRewrite ( Server.Transfer ) en utilisant les vues Razor. Je préférerais retourner un 404 avec une page d’erreur personnalisée pour des raisons de référencement.

Une partie de ceci est nouvelle prise sur la technique de cottsak ci-dessus.

Cette solution utilise également des parameters web.config minimaux favorisant les filtres d’erreur MVC 3.

Usage

Il suffit de lancer une exception HttpException à partir d’une action ou d’un ActionFilterAtsortingbute personnalisé.

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

Étape 1

Ajoutez le paramètre suivant à votre fichier web.config. Cela est nécessaire pour utiliser HandleErrorAtsortingbute de MVC.

  

Étape 2

Ajoutez un HandleHttpErrorAtsortingbute personnalisé similaire à HandleErrorAtsortingbute du framework MVC, à l’exception des erreurs HTTP:

  Public Class HandleHttpErrorAtsortingbute Inherits FilterAtsortingbute Implements IExceptionFilter Private Const m_DefaultViewFormat As Ssortingng = "ErrorHttp{0}" Private m_HttpCode As HttpStatusCode Private m_Master As Ssortingng Private m_View As Ssortingng 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 Ssortingng Get Return If(m_Master, Ssortingng.Empty) End Get Set(value As Ssortingng) m_Master = value End Set End Property Public Property View As Ssortingng Get If Ssortingng.IsNullOrEmpty(m_View) Then Return Ssortingng.Format(m_DefaultViewFormat, Me.HttpCode) End If Return m_View End Get Set(value As Ssortingng) 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 au GlobalFilterCollection ( GlobalFilters.Filters ) dans Global.asax . Cet exemple acheminera toutes les erreurs InternalServerError (500) vers la vue partagée Error ( 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’object System.Web.Mvc.HandleErrorInfo comme modèle.

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

Étape 4

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

 Public Class BaseController Inherits System.Web.Mvc.Controller Protected Overrides Sub HandleUnknownAction(actionName As Ssortingng) 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 un remplacement ControllerFactory et remplacez-le dans votre fichier Global.asax dans Application_Start. This step allows us to raise the HTTP 404 exception when an invalid controller name has been specified.

 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

Include a special route in your RoutTable.Routes for the BaseController Unknown action. This will help us raise a 404 in the case where a user accesses an unknown controller, or unknown action.

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

Résumé

This example demonstrated how one can use the MVC framework to return 404 Http Error Codes to the browser without a redirect using filter atsortingbutes and shared error views. It also demonstrates showing the same custom error page when invalid controller names and action names are specified.

I’ll add a screenshot of an invalid controller name, action name, and a custom 404 raised from the Home/TriggerNotFound action if I get enough votes to post one =). Fiddler returns a 404 message when I access the following URLs using this solution:

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

cottsak’s post above and these articles were good references.

  • Problems using CustomErrors redirectMode=ResponseRewrite
  • Elmah + MVC HandleErrorAtsortingbute

My shortened solution that works with unhandled areas, controllers and actions:

  1. Create a view 404.cshtml.

  2. Create a base class for your controllers:

     public class Controller : System.Web.Mvc.Controller { protected override void HandleUnknownAction(ssortingng actionName) { Http404().ExecuteResult(ControllerContext); } protected virtual ViewResult Http404() { Response.StatusCode = (int)HttpStatusCode.NotFound; return View("404"); } } 
  3. Create a custom controller factory returning your base controller as a fallback:

     public class ControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType != null) return base.GetControllerInstance(requestContext, controllerType); return new Controller(); } } 
  4. Add to Application_Start() the following line:

     ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory)); 

In MVC4 WebAPI 404 can be handle in the following way,

COURSES APICONTROLLER

  // GET /api/courses/5 public HttpResponseMessage Get(int id) { HttpResponseMessage resp = null; var aCourse = _courses.Where(c => c.Id == id).FirstOrDefault(); resp = aCourse == null ? new HttpResponseMessage(System.Net.HttpStatusCode.NotFound) : new HttpResponseMessage(aCourse); return resp; } 

HOME CONTROLLER

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

VUE

 

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

RESULTS

entrer la description de l'image ici

Try NotFoundMVC on nuget. It works , no setup.

My solution, in case someone finds it useful.

In Web.config:

     ...  

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

Add a PageNotFound.cshtml in the Shared folder, and that’s it.

It seems to me that the standard CustomErrors configuration should just work however, due to the reliance on Server.Transfer it seems that the internal implementation of ResponseRewrite isn’t compatible with MVC.

This feels like a glaring functionality hole to me, so I decided to re-implement this feature using a HTTP module. The solution below allows you to handle any HTTP status code (including 404) by redirecting to any valid MVC route just as you would do normally.

     

This has been tested on the following platforms;

  • MVC4 in Integrated Pipeline Mode (IIS Express 8)
  • MVC4 in Classic Mode (VS Development Server, Cassini)
  • MVC4 in Classic Mode (IIS6)

Avantages

  • Generic solution which can be dropped into any MVC project
  • Enables support for traditional custom errors configuration
  • Works in both Integrated Pipeline and Classic modes

La solution

 namespace Foo.Bar.Modules { ///  /// Enables support for CustomErrors ResponseRewrite mode in MVC. ///  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 errorPaths = new Dictionary(); 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)) { ssortingng 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() { } } } 

Usage

Include this as the final HTTP module in your web.config

             

For those of you paying attention you will notice that in Integrated Pipeline mode this will always respond with HTTP 200 due to the way Server.TransferRequest works. To return the proper error code I use the following error controller.

 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.ToSsortingng()); } } 

Dealing with errors in ASP.NET MVC is just a pain in the butt. I sortinged a whole lot of suggestions on this page and on other questions and sites and nothing works good. One suggestion was to handle errors on web.config inside system.webserver but that just returns blank pages .

My goal when coming up with this solution was to;

  • NOT REDIRECT
  • Return PROPER STATUS CODES not 200/Ok like the default error handling

Voici ma solution

1 .Add the following to system.web section

        

The above handles any urls not handled by routes.config and unhandled exceptions especially those encountered on the views. Notice I used aspx not html . This is so I can add a response code on the code behind.

2 . Create a folder called Error (or whatever you prefer) at the root of your project and add the two webforms. Below is my 404 page;

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

404 - Page Not found

The page you are looking for cannot be found.


And on the code behind I set the response code

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

Do the same for the 500 page

3 .To handle errors within the controllers. There’s many ways to do it. C’est ce qui a fonctionné pour moi. All my controllers inherit from a base controller. In the base controller, I have the following methods

 protected ActionResult ShowNotFound() { return ShowNotFound("Page not found...."); } protected ActionResult ShowNotFound(ssortingng message) { return ShowCustomError(HttpStatusCode.NotFound, message); } protected ActionResult ShowServerError() { return ShowServerError("Application error...."); } protected ActionResult ShowServerError(ssortingng message) { return ShowCustomError(HttpStatusCode.InternalServerError, message); } protected ActionResult ShowNotAuthorized() { return ShowNotAuthorized("You are not allowed ...."); } protected ActionResult ShowNotAuthorized(ssortingng message) { return ShowCustomError(HttpStatusCode.Forbidden, message); } protected ActionResult ShowCustomError(HttpStatusCode statusCode, ssortingng message) { Response.StatusCode = (int)statusCode; ssortingng 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 .Add the CustomError.cshtml to your Shared views folder. Below is mine;

 

@ViewBag.Title


@ViewBag.Message

Now in your application controller you can do something like this;

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

Now for the caveat . It won’t handle static file errors. So if you have a route such as example.com/widgets and the user changes it to example.com/widgets.html , they will get the IIS default error page so you have to handle IIS level errors some other way.

Posting an answer since my comment was too long…

It’s both a comment and questions to the unicorn post/answer:

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

I prefer this answer over the others for it’s simplicity and the fact that apparently some folks at Microsoft were consulted. I got three questions however and if they can be answered then I will call this answer the holy grail of all 404/500 error answers on the interwebs for an ASP.NET MVC (x) app.

@Pure.Krome

  1. Can you update your answer with the SEO stuff from the comments pointed out by GWB (there was never any mentioning of this in your answer) – and ?

  2. Can you ask your ASP.NET team friends if it is okay to do it like that – would be nice to have some confirmation – maybe it’s a big no-no to change redirectMode and existingResponse in this way to be able to play nicely with SEO?!

  3. Can you add some clarification surrounding all that stuff ( customErrors redirectMode="ResponseRewrite" , customErrors redirectMode="ResponseRedirect" , httpErrors errorMode="Custom" existingResponse="Replace" , REMOVE customErrors COMPLETELY as someone suggested) after talking to your friends at Microsoft?

As I was saying; it would be supernice if we could make your answer more complete as this seem to be a fairly popular question with 54 000+ views.

Update : Unicorn answer does a 302 Found and a 200 OK and cannot be changed to only return 404 using a route. It has to be a physical file which is not very MVC:ish. So moving on to another solution. Too bad because this seemed to be the ultimate MVC:ish answer this far.

Adding my solution, which is almost identical to Herman Kan’s, with a small wrinkle to allow it to work for my project.

Create a custom error controller:

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

Then create a custom controller factory:

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

Finally, add an override to the custom error controller:

 protected override void HandleUnknownAction(ssortingng 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. No need for Web.config changes.

1) Make abstract Controller class.

 public abstract class MyController:Controller { public ActionResult NotFound() { Response.StatusCode = 404; return View("NotFound"); } protected override void HandleUnknownAction(ssortingng actionName) { this.ActionInvoker.InvokeAction(this.ControllerContext, "NotFound"); } protected override void OnAuthorization(AuthorizationContext filterContext) { } } 

2) Make inheritence from this abstract class in your all controllers

 public class HomeController : MyController {} 

3) And add a view named “NotFound” in you View-Shared folder.

I went through most of the solutions posted on this thread. While this question might be old, it is still very applicable to new projects even now, so I spent quite a lot of time reading up on the answers presented here as well as else where.

As @Marco pointed out the different cases under which a 404 can happen, I checked the solution I comstackd together against that list. In addition to his list of requirements, I also added one more.

  • The solution should be able to handle MVC as well as AJAX/WebAPI calls in the most appropriate manner. (ie if 404 happens in MVC, it should show the Not Found page and if 404 happens in WebAPI, it should not hijack the XML/JSON response so that the consuming Javascript can parse it easily).

This solution is 2 fold:

First part of it comes from @Guillaume at https://stackoverflow.com/a/27354140/2310818 . Their solution takes care of any 404 that were caused due to invalid route, invalid controller and invalid action.

The idea is to create a WebForm and then make it call the NotFound action of your MVC Errors Controller. It does all of this without any redirect so you will not see a single 302 in Fiddler. The original URL is also preserved, which makes this solution fantastic!


Second part of it comes from @Germán at https://stackoverflow.com/a/5536676/2310818 . Their solution takes care of any 404 returned by your actions in the form of HttpNotFoundResult() or throw new HttpException()!

The idea is to have a filter look at the response as well as the exception thrown by your MVC controllers and to call the appropriate action in your Errors Controller. Again this solution works without any redirect and the original url is preserved!


As you can see, both of these solutions together offer a very robust error handling mechanism and they achieve all the requirements listed by @Marco as well as my requirements. If you would like to see a working sample or a demo of this solution, please leave in the comments and I would be happy to put it together.

I have gone through all articles but nothing works for me: My requirement user type anything in your url custom 404 page should show.I thought it is very straight forward.But you should understand handling of 404 properly:

             

I found this article very helpfull.should be read at once. Custome error page-Ben Foster