wake-up-neo.com

Wie kann ich mit 404 in ASP.NET MVC richtig umgehen?

Ich benutze RC2

Verwenden von URL-Routing:

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

Das Obige scheint sich um Anfragen wie diese zu kümmern (vorausgesetzt, die Standardroutentabellen werden vom anfänglichen MVC-Projekt eingerichtet): "/ blah/blah/blah/blah"

Überschreibt HandleUnknownAction () im Controller selbst:

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

Die vorherigen Strategien behandeln jedoch keine Anforderung an einen fehlerhaften/unbekannten Controller. Zum Beispiel habe ich kein "/ IDoNotExist". Wenn ich dies anfordere, erhalte ich die generische 404-Seite vom Webserver und nicht meine 404, wenn ich Routing + Override verwende.

Meine Frage lautet schließlich: Gibt es eine Möglichkeit, diese Art von Anforderung über eine Route oder etwas anderes im MVC-Framework selbst abzufangen?

ODER sollte ich einfach standardmäßig Web.Config customErrors als meinen 404-Handler verwenden und all dies vergessen? Ich gehe davon aus, dass ich, wenn ich mich für customErrors entscheide, die generische 404-Seite aufgrund der Einschränkungen von Web.Config für den direkten Zugriff außerhalb von/Views speichern muss.

424
Brian

Der Code stammt aus http://blogs.Microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2 .aspx und funktioniert auch in ASP.net MVC 1.0

So gehe ich mit http-Ausnahmen um:

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

Voraussetzungen für 404

Das Folgende sind meine Anforderungen für eine 404-Lösung und unten zeige ich, wie ich es implementiere:

  • Ich möchte abgestimmte Routen mit schlechten Aktionen behandeln
  • Ich möchte abgestimmte Routen mit fehlerhaften Controllern verarbeiten
  • Ich möchte mit nicht übereinstimmenden Routen umgehen (willkürliche URLs, die meine App nicht verstehen kann) - ich möchte nicht, dass diese in die Global.asax oder IIS weil dann Ich kann nicht richtig zurück in meine MVC-App umleiten
  • Ich möchte eine Möglichkeit, wie oben beschrieben mit benutzerdefinierten 404-Werten umzugehen - beispielsweise, wenn eine ID für ein Objekt gesendet wird, das nicht vorhanden ist (möglicherweise gelöscht).
  • Ich möchte, dass alle meine 404s eine MVC-Ansicht (keine statische Seite) zurückgeben, auf die ich bei Bedarf später mehr Daten pumpen kann ( gute 404-Designs ) und sie = muss gibt den HTTP 404-Statuscode zurück

Lösung

Ich denke, Sie sollten Application_Error In der Global.asax für höhere Dinge speichern, wie unbehandelte Ausnahmen und Protokollierung (wie Shay Jacobys Antwort zeigt), aber nicht 404-Behandlung. Dies ist der Grund, warum mein Vorschlag das 404-Zeug aus der Datei Global.asax heraushält.

Schritt 1: Haben Sie einen gemeinsamen Platz für die 404-Fehlerlogik

Dies ist eine gute Idee für die Wartbarkeit. Verwenden Sie einen ErrorController , damit zukünftige Verbesserungen an Ihrer gut gestalteten 404-Seite leicht angepasst werden können. Stellen Sie außerdem sicher, dass Ihre Antwort den 404-Code enthält!

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
}

Schritt 2: Verwenden Sie eine Basis-Controller-Klasse, damit Sie auf einfache Weise Ihre benutzerdefinierte 404-Aktion aufrufen und HandleUnknownAction verkabeln können.

404s in ASP.NET MVC müssen an mehreren Stellen abgefangen werden. Der erste ist HandleUnknownAction.

Die Methode InvokeHttp404 Erstellt einen gemeinsamen Ort für die Umleitung zu ErrorController und unserer neuen Aktion Http404. Denken Sie TROCKEN !

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
}

Schritt 3: Verwenden Sie Dependency Injection in Ihrer Controller Factory und verbinden Sie 404 HttpExceptions

Wie so (es muss nicht StructureMap sein):

MVC1.0 Beispiel:

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

                return errorController;
            }
            else
                throw ex;
        }

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

MVC2.0 Beispiel:

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

Ich denke, es ist besser, Fehler näher an ihrem Ursprung zu erfassen. Aus diesem Grund ziehe ich das oben genannte dem Application_Error - Handler vor.

Dies ist der zweite Ort, um 404 zu fangen.

Schritt 4: Fügen Sie Global.asax eine NotFound-Route für URLs hinzu, die in Ihrer App nicht analysiert werden können

Diese Route sollte auf unsere Aktion Http404 Verweisen. Beachten Sie, dass der Parameter url eine relative URL ist, da das Routingmodul hier den Domänenteil entfernt. Aus diesem Grund haben wir in Schritt 1 alle diese bedingten URL-Logik.

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

Dies ist der dritte und letzte Ort, an dem Sie in einer MVC-App, die Sie nicht selbst aufrufen, 404-Werte abfangen können. Wenn Sie hier keine unvergleichlichen Routen abfangen, leitet MVC das Problem an ASP.NET (Global.asax) weiter, und Sie möchten dies in dieser Situation nicht wirklich.

Schritt 5: Rufen Sie abschließend 404s auf, wenn Ihre App etwas nicht finden kann

Zum Beispiel, wenn eine ungültige ID an meinen Kreditsachbearbeiter gesendet wird (abgeleitet von 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);
    }

Es wäre schön, wenn dies alles an weniger Stellen mit weniger Code verbunden werden könnte, aber ich denke, diese Lösung ist wartbarer, testbarer und ziemlich pragmatisch.

Vielen Dank für das bisherige Feedback. Ich würde gerne mehr bekommen.

HINWEIS: Dies wurde erheblich von meiner ursprünglichen Antwort geändert, aber der Zweck/die Anforderungen sind die gleichen - aus diesem Grund habe ich keine neue Antwort hinzugefügt

252
Matt Kocaj

ASP.NET MVC unterstützt benutzerdefinierte 404-Seiten nicht sehr gut. Custom-Controller-Factory, Sammelstrecke, Basis-Controller-Klasse mit HandleUnknownAction - argh!

Bisher sind benutzerdefinierte IIS-Fehlerseiten die bessere Alternative:

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

Beispielprojekt

232
Pavel Chuchuva

Schnelle Antwort/TL; DR

enter image description here

Für die Faulen da draußen:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

Dann entferne diese Zeile aus global.asax

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

Und dies gilt nur für IIS7 + und IIS Express.

Wenn du Cassini verwendest ... na ja ... ähm ... umständlich ... awkward


Lange, erklärte Antwort

Ich weiß, dass dies beantwortet wurde. Aber die Antwort ist WIRKLICH EINFACH (Beifall an David Fowler und Damian Edwards , dass sie dies wirklich beantwortet haben).

Es gibt keine Notwendigkeit, etwas Benutzerdefiniertes zu tun .

Für ASP.NET MVC3 Sind alle Teile vorhanden.

Schritt 1 -> Aktualisieren Sie Ihre web.config an ZWEI Stellen.

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

und

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

Beachten Sie nun genau die ROUTEN, für die ich mich entschieden habe. Sie können alles verwenden, aber meine Routen sind

  • /NotFound <- für eine 404 nicht gefundene Fehlerseite.
  • /ServerError <- Beziehen Sie bei allen anderen Fehlern Fehler ein, die in meinem Code auftreten. Dies ist ein 500 Internal Server Error

Sehen Sie, wie der erste Abschnitt in <system.web> Nur einen benutzerdefinierten Eintrag hat? Der Eintrag statusCode="404"? Ich habe nur einen Statuscode aufgelistet, da alle anderen Fehler, einschließlich des 500 Server Error (Dh der lästige Fehler, der auftritt, wenn Ihr Code einen Fehler aufweist und die Benutzeranforderung zum Absturz bringt). Alle anderen Fehler werden von behandelt die Einstellung defaultRedirect="/ServerError" .. die besagt, wenn Sie keine 404-Seite gefunden haben, dann gehen Sie bitte zur Route /ServerError.

Okay. das ist aus dem Weg .. jetzt zu meinen Routen in global.asax

Schritt 2 - Erstellen der Routen in Global.asax

Hier ist mein vollständiger Streckenabschnitt.

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

Das listet zwei Ignorierrouten auf -> axd's Und favicons (ooo! Bonus Ignorierroute, für Sie!) Dann (und die Reihenfolge ist HIER UNBEDINGT) habe ich meine zwei expliziten Fehlerbehandlungsrouten. gefolgt von anderen Routen. In diesem Fall die Standardeinstellung. Natürlich habe ich mehr, aber das ist etwas Besonderes für meine Website. Stellen Sie einfach sicher, dass die Fehlerrouten ganz oben in der Liste stehen. Ordnung ist unabdingbar .

Während wir uns in unserer global.asax - Datei befinden, registrieren wir das HandleError-Attribut NICHT global. Nein, nein, nein, Sir. Nadda. Nee. Nien. Negativ. Noooooooooo ...

Entfernen Sie diese Zeile aus global.asax

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

Schritt 3 - Erstellen Sie den Controller mit den Aktionsmethoden

Nun ... fügen wir einen Controller mit zwei Aktionsmethoden hinzu ...

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, lass uns das überprüfen. Zunächst gibt es hier [~ # ~] kein [~ # ~] [HandleError] Attribut. Warum? Da das eingebaute ASP.NET - Framework bereits Fehler behandelt UND wir alle erforderlichen Informationen zur Behandlung eines Fehlers angegeben haben :) Es ist in dieser Methode!

Als nächstes habe ich die beiden Aktionsmethoden. Nichts hartes da. Wenn Sie Ausnahmeinformationen anzeigen möchten, können Sie Server.GetLastError() verwenden, um diese Informationen abzurufen.

Bonus WTF: Ja, ich habe eine dritte Aktionsmethode durchgeführt, um die Fehlerbehandlung zu testen.

Schritt 4 - Erstellen Sie die Ansichten

Erstellen Sie schließlich zwei Ansichten. Bringen Sie sie für diesen Controller in die normale Position.

enter image description here

Bonus Kommentare

  • Sie brauchen kein Application_Error(object sender, EventArgs e)
  • Die obigen Schritte funktionieren alle zu 100% perfekt mit Elmah . Elmah brutzelt Wroxs!

Und das, meine Freunde, sollte es sein.

Nun, herzlichen Glückwunsch zum Lesen und ein Einhorn als Preis!

enter image description here

153
Pure.Krome

Ich habe VIEL untersucht, wie man 404s in MVC richtig verwaltet (speziell MVC3) und dies, IMHO ist die beste Lösung, die ich mir ausgedacht habe:

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

(Optional)

Erklärung:

AFAIK, es gibt 6 verschiedene Fälle, in denen ASP.NET MVC3-Apps 404-Werte generieren können.

(Automatisch generiert von ASP.NET Framework:)

(1) Eine URL findet keine Übereinstimmung in der Routentabelle.

(Wird automatisch von ASP.NET MVC Framework generiert:)

(2) Eine URL findet eine Übereinstimmung in der Routentabelle, gibt jedoch einen nicht vorhandenen Controller an.

(3) Eine URL findet eine Übereinstimmung in der Routentabelle, gibt jedoch eine nicht vorhandene Aktion an.

(manuell generiert:)

(4) Eine Aktion gibt ein HttpNotFoundResult mit der Methode HttpNotFound () zurück.

(5) Eine Aktion löst eine HttpException mit dem Statuscode 404 aus.

(6) Durch eine Aktion wird die Response.StatusCode-Eigenschaft manuell in 404 geändert.

Normalerweise möchten Sie 3 Ziele erreichen:

(1) Zeigt dem Benutzer eine benutzerdefinierte 404-Fehlerseite an.

(2) Pflegen Sie den 404-Statuscode in der Kundenantwort (besonders wichtig für SEO).

(3) Senden Sie die Antwort direkt, ohne eine 302-Umleitung zu erfordern.

Es gibt verschiedene Möglichkeiten, dies zu erreichen:

(1)

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

Probleme mit dieser Lösung:

  1. Entspricht nicht dem Ziel (1) in den Fällen (1), (4), (6).
  2. Entspricht nicht automatisch Ziel (2). Es muss manuell programmiert werden.
  3. Entspricht nicht dem Ziel (3).

(2)

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

Probleme mit dieser Lösung:

  1. Funktioniert nur mit IIS 7+.
  2. Entspricht nicht dem Ziel (1) in den Fällen (2), (3), (5).
  3. Entspricht nicht automatisch Ziel (2). Es muss manuell programmiert werden.

(3)

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

Probleme mit dieser Lösung:

  1. Funktioniert nur mit IIS 7+.
  2. Entspricht nicht automatisch Ziel (2). Es muss manuell programmiert werden.
  3. Es verdeckt http-Ausnahmen auf Anwendungsebene. Z.B. Der Abschnitt customErrors, System.Web.Mvc.HandleErrorAttribute usw. können nicht verwendet werden. Es können nicht nur allgemeine Fehlerseiten angezeigt werden.

(4)

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

und

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

Probleme mit dieser Lösung:

  1. Funktioniert nur mit IIS 7+.
  2. Entspricht nicht automatisch Ziel (2). Es muss manuell programmiert werden.
  3. Entspricht nicht Ziel (3) in den Fällen (2), (3), (5).

Leute, die sich zuvor damit befasst haben, haben sogar versucht, ihre eigenen Bibliotheken zu erstellen (siehe http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html ). Die vorherige Lösung scheint jedoch alle Fälle abzudecken, ohne die Komplexität der Verwendung einer externen Bibliothek.

85
Marco

Ich mag die Lösung von Cottsaks sehr und denke, sie ist sehr klar erklärt. Meine einzige Ergänzung war, Schritt 2 wie folgt zu ändern

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
}

Grundsätzlich verhindert dies, dass URLs mit ungültigen Aktionen UND Controllern die Ausnahmeroutine zweimal auslösen. zB für URLs wie asdfsdf/dfgdfgd

13
Dave Lowe

Die einzige Möglichkeit, die @ cottsak-Methode für ungültige Controller zu verwenden, bestand darin, die vorhandene Routenanforderung in der CustomControllerFactory wie folgt zu ändern:

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

Ich sollte erwähnen, dass ich MVC 2.0 verwende.

6
Dave K

Hier ist eine weitere Methode, die MVC-Tools verwendet, mit denen Sie Anforderungen an ungültige Controllernamen, ungültige Routennamen und andere Kriterien, die Sie als Teil einer Aktionsmethode betrachten, verarbeiten können. Ich persönlich bevorzuge es, so viele web.config-Einstellungen wie möglich zu vermeiden, da sie die 302/200-Umleitung ausführen und ResponseRewrite (Server.Transfer) Mit Razor-Ansichten nicht unterstützen. Ich würde es vorziehen, aus SEO-Gründen einen 404 mit einer benutzerdefinierten Fehlerseite zurückzugeben.

Einiges davon ist neu in Bezug auf Cottsaks obige Technik.

Diese Lösung verwendet stattdessen auch minimale web.config-Einstellungen, die die MVC 3-Fehlerfilter bevorzugen.

Verwendungszweck

Wirf einfach eine HttpException aus einer Aktion oder einem benutzerdefinierten ActionFilterAttribute.

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

Schritt 1

Fügen Sie Ihrer web.config die folgende Einstellung hinzu. Dies ist erforderlich, um das HandleErrorAttribute von MVC zu verwenden.

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

Schritt 2

Fügen Sie ein benutzerdefiniertes HandleHttpErrorAttribute hinzu, das mit Ausnahme von HTTP-Fehlern dem HandleErrorAttribute des MVC-Frameworks ähnelt:

<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

Schritt 3

Fügen Sie der GlobalFilterCollection (GlobalFilters.Filters) In Global.asax Filter hinzu. In diesem Beispiel werden alle InternalServerError (500) -Fehler an die gemeinsam genutzte Fehleransicht (Views/Shared/Error.vbhtml) Weitergeleitet. NotFound (404) -Fehler werden auch in den freigegebenen Ansichten an ErrorHttp404.vbhtml gesendet. Ich habe hier einen 401-Fehler hinzugefügt, um Ihnen zu zeigen, wie dieser für zusätzliche HTTP-Fehlercodes erweitert werden kann. Beachten Sie, dass dies geteilte Ansichten sein müssen und alle das Objekt System.Web.Mvc.HandleErrorInfo Als Modell verwenden.

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

Schritt 4

Erstellen Sie eine Basis-Controller-Klasse und erben Sie diese in Ihren Controllern. Mit diesem Schritt können wir unbekannte Aktionsnamen verarbeiten und den HTTP 404-Fehler auf unser HandleHttpErrorAttribute auslösen.

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

Schritt 5

Erstellen Sie eine ControllerFactory-Überschreibung und überschreiben Sie sie in Ihrer Global.asax-Datei in Application_Start. Mit diesem Schritt können wir die HTTP 404-Ausnahme auslösen, wenn ein ungültiger Controllername angegeben wurde.

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)

Schritt 6

Fügen Sie Ihrer RoutTable.Routes eine spezielle Route für die Aktion BaseController Unknown hinzu. Auf diese Weise können wir einen 404-Wert auslösen, wenn ein Benutzer auf einen unbekannten Controller oder eine unbekannte Aktion zugreift.

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

Zusammenfassung

In diesem Beispiel wurde gezeigt, wie mithilfe des MVC-Frameworks 404-HTTP-Fehlercodes ohne Umleitung mithilfe von Filterattributen und gemeinsam genutzten Fehleransichten an den Browser zurückgegeben werden können. Es wird auch gezeigt, wie dieselbe benutzerdefinierte Fehlerseite angezeigt wird, wenn ungültige Controller- und Aktionsnamen angegeben werden.

Ich füge einen Screenshot eines ungültigen Controllernamens, eines Aktionsnamens und eines benutzerdefinierten 404 hinzu, der aus der Aktion Home/TriggerNotFound generiert wurde, wenn ich genügend Stimmen für das Posten von 1 bekomme =). Fiddler gibt eine 404-Nachricht zurück, wenn ich mit dieser Lösung auf die folgenden URLs zugreife:

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

der obige Beitrag von cottsak und diese Artikel waren gute Referenzen.

4
sky-dev

Meine verkürzte Lösung, die mit nicht bearbeiteten Bereichen, Controllern und Aktionen funktioniert:

  1. Erstellen Sie eine Ansicht 404.cshtml.

  2. Erstellen Sie eine Basisklasse für Ihre Controller:

    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. Erstellen Sie eine benutzerdefinierte Controller-Factory, die Ihren Basis-Controller als Fallback zurückgibt:

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
    
  4. Fügen Sie zu Application_Start() die folgende Zeile hinzu:

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

In MVC4 kann WebAPI 404 folgendermaßen gehandhabt werden:

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

HOME CONTROLLER

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

AUSSICHT

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

ERGEBNISSE

enter image description here

3
Diganta Kumar

Versuchen Sie NotFoundMVC auf Nuget. Es funktioniert, kein Setup.

2
DarthVader

Es scheint mir, dass die Standardkonfiguration CustomErrorssollte nur funktionieren jedoch aufgrund des Vertrauens auf Server.Transfer Es scheint, dass die interne Implementierung von ResponseRewrite nicht mit MVC kompatibel ist.

Das fühlt sich für mich wie eine krasse Funktionslücke an, deshalb habe ich mich entschieden, diese Funktion mithilfe eines HTTP-Moduls erneut zu implementieren. Mit der folgenden Lösung können Sie jeden HTTP-Statuscode (einschließlich 404) verarbeiten, indem Sie wie gewohnt auf eine gültige MVC-Route umleiten.

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

Dies wurde auf den folgenden Plattformen getestet;

  • MVC4 im integrierten Pipeline-Modus (IIS Express 8)
  • MVC4 im klassischen Modus (VS Development Server, Cassini)
  • MVC4 im klassischen Modus (IIS6)

Vorteile

  • Generische Lösung, die in jedes MVC-Projekt eingefügt werden kann
  • Aktiviert die Unterstützung für herkömmliche benutzerdefinierte Fehlerkonfigurationen
  • Funktioniert sowohl im integrierten Pipeline- als auch im klassischen Modus

Die Lösung

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

        }


    }

}

Verwendung

Fügen Sie dies als letztes HTTP-Modul in Ihre web.config ein

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

Wenn Sie aufpassen, werden Sie feststellen, dass dies im integrierten Pipeline-Modus aufgrund der Art und Weise, wie Server.TransferRequest funktioniert. Um den richtigen Fehlercode zurückzugeben, verwende ich den folgenden Fehlercontroller.

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

Meine Lösung für den Fall, dass jemand es nützlich findet.

In Web.config:

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

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

Füge hinzu ein PageNotFound.cshtml im Shared Ordner, und das war's.

2
Konamiman

Der Umgang mit Fehlern in ASP.NET MVC ist nur ein Ärgernis. Ich habe viele Vorschläge auf dieser Seite und auf anderen Fragen und Websites ausprobiert und nichts funktioniert gut. Ein Vorschlag war, Fehler in web.config innerhalb system.webserver zu behandeln. aber das ist nur gibt leere Seiten zurück.

Mein Ziel bei der Entwicklung dieser Lösung war:

  • NICHT WEITERLEITEN
  • RÜCKGABE EIGENER STATUSCODES nicht 200/OK wie die Standardfehlerbehandlung

Hier ist meine Lösung.

1. Fügen Sie Folgendes zum Abschnitt system.web hinzu

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

Die obigen Anweisungen behandeln alle URLs, die nicht von routes.config behandelt werden, sowie nicht behandelte Ausnahmen, insbesondere die in den Ansichten aufgetretenen. Beachten Sie, dass ich aspx nicht html verwendet habe. Auf diese Weise kann ich dem Code dahinter einen Antwortcode hinzufügen.

2. Erstellen Sie einen Ordner mit dem Namen Error (oder was auch immer Sie bevorzugen) im Stammverzeichnis Ihres Projekts und fügen Sie die beiden Webformulare hinzu. Unten ist meine 404 Seite;

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

Und auf den Code dahinter habe ich den Antwortcode gesetzt

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

Machen Sie dasselbe für die 500-Seite

. Um Fehler innerhalb der Steuerungen zu behandeln. Es gibt viele Möglichkeiten, dies zu tun. Das hat bei mir funktioniert. Alle meine Controller erben von einem Basiscontroller. In der Basissteuerung habe ich die folgenden Methoden

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. Fügen Sie die Datei CustomError.cshtml zu Ihrem freigegebenen Ansichtsordner hinzu. Unten ist meins;

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

Jetzt können Sie in Ihrem Anwendungs-Controller so etwas tun.

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

Nun zum Vorbehalt. Es werden keine statischen Dateifehler behandelt. Wenn Sie also eine Route wie example.com/widgets haben und der Benutzer diese in example.com/ ändert widgets.html erhalten sie die Standardfehlerseite IIS), so dass Sie mit IIS Level-Fehlern auf andere Weise umgehen müssen .

2
Moses Machua

Ich poste eine Antwort, da mein Kommentar zu lang war ...

Es ist sowohl ein Kommentar als auch eine Frage an den Unicorn-Post/die Antwort:

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

Ich bevorzuge diese Antwort gegenüber den anderen, weil sie einfach ist und weil anscheinend einige Leute bei Microsoft konsultiert wurden. Ich habe jedoch drei Fragen, und wenn diese beantwortet werden können, werde ich diese Antwort als das Allerheiligste aller 404/500-Fehlerantworten im Internet für eine ASP.NET MVC (x) -Anwendung bezeichnen.

@ Pure.Krome

  1. Können Sie Ihre Antwort mit dem SEO-Material aus den Kommentaren aktualisieren, auf die GWB hingewiesen hat (dies wurde in Ihrer Antwort nie erwähnt) - <customErrors mode="On" redirectMode="ResponseRewrite"> und <httpErrors errorMode="Custom" existingResponse="Replace">?

  2. Können Sie Ihre ASP.NET-Team-Freunde fragen, ob dies in Ordnung ist? Wäre schön, wenn Sie eine Bestätigung hätten? Vielleicht ist es ein großes Nein, wenn Sie redirectMode und existingResponse ändern auf diese Weise in der Lage sein, gut mit SEO zu spielen ?!

  3. Können Sie etwas Klarheit über all das Zeug hinzufügen (customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", ENTFERNEN customErrors VOLLSTÄNDIG, wie jemand vorgeschlagen hat), nachdem Sie mit Ihren Freunden bei Microsoft gesprochen haben?

Wie ich sagte; Es wäre großartig, wenn wir Ihre Antwort vervollständigen könnten, da dies eine recht beliebte Frage mit über 54 000 Ansichten zu sein scheint.

Update : Die Unicorn-Antwort führt einen 302 Found und einen 200 OK aus und kann nicht so geändert werden, dass nur 404 über eine Route zurückgegeben wird. Es muss eine physische Datei sein, die nicht sehr MVC: ish ist. Wechseln Sie also zu einer anderen Lösung. Schade, denn dies schien die ultimative MVC zu sein: ish Antwort so weit.

1
PussInBoots

Hinzufügen meiner Lösung, die fast identisch mit der von Herman Kan ist, mit einer kleinen Falte, damit sie für mein Projekt funktioniert.

Erstellen Sie einen benutzerdefinierten Fehlercontroller:

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

Erstellen Sie dann eine benutzerdefinierte Controller-Factory:

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

Fügen Sie schließlich dem benutzerdefinierten Fehlercontroller eine Außerkraftsetzung hinzu:

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

Und das ist es. Keine Notwendigkeit für Web.config-Änderungen.

1
Rob Lyndon

1) Machen Sie abstrakte Controller-Klasse.

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) Vererben Sie diese abstrakte Klasse in allen Controllern

public class HomeController : MyController
{}  

3) Fügen Sie eine Ansicht mit dem Namen "Nicht gefunden" in Ihren View-Shared-Ordner ein.

1
Mehmet

Ich habe die meisten Lösungen in diesem Thread durchgesehen. Obwohl diese Frage alt sein mag, ist sie auch jetzt noch sehr gut auf neue Projekte anwendbar, so dass ich viel Zeit damit verbracht habe, die hier präsentierten Antworten und auch woanders nachzulesen.

Als @Marco auf die verschiedenen Fälle hinwies, in denen ein 404 auftreten kann, überprüfte ich die von mir zusammengestellte Lösung anhand dieser Liste. Zusätzlich zu seiner Anforderungsliste habe ich noch eine hinzugefügt.

  • Die Lösung sollte MVC- und AJAX/WebAPI-Aufrufe am besten verarbeiten können. (d. h., wenn 404 in MVC auftritt, sollte die Seite Nicht gefunden angezeigt werden, und wenn 404 in WebAPI auftritt, sollte die XML/JSON-Antwort nicht entführt werden, damit das aufwendige Javascript sie problemlos analysieren kann.).

Diese Lösung ist zweifach:

Der erste Teil stammt von @Guillaume unter https://stackoverflow.com/a/27354140/2310818 . Ihre Lösung kümmert sich um alle 404, die aufgrund einer ungültigen Route, eines ungültigen Controllers und einer ungültigen Aktion verursacht wurden.

Die Idee ist, ein WebForm zu erstellen und es dann die NotFound-Aktion Ihres MVC Errors Controllers aufrufen zu lassen. All dies geschieht ohne Weiterleitung, sodass Sie in Fiddler keinen einzigen 302 sehen. Die ursprüngliche URL bleibt ebenfalls erhalten, was diese Lösung fantastisch macht!


Der zweite Teil stammt von @ Germán unter https://stackoverflow.com/a/5536676/2310818 . Ihre Lösung kümmert sich um alle 404, die von Ihren Aktionen in Form von HttpNotFoundResult () zurückgegeben werden, oder wirft eine neue HttpException ()!

Die Idee ist, die Antwort sowie die von Ihren MVC-Controllern ausgelöste Ausnahme zu filtern und die entsprechende Aktion in Ihrem Fehler-Controller aufzurufen. Auch diese Lösung funktioniert ohne Weiterleitung und die ursprüngliche URL bleibt erhalten!


Wie Sie sehen, bieten beide Lösungen zusammen einen sehr robusten Fehlerbehandlungsmechanismus und erfüllen alle von @Marco aufgeführten Anforderungen sowie meine Anforderungen. Wenn Sie ein funktionierendes Beispiel oder eine Demo dieser Lösung sehen möchten, lassen Sie es bitte in den Kommentaren und ich würde es gerne zusammenstellen.

0
Parth Shah

Ich habe alle Artikel durchgesehen, aber für mich funktioniert nichts: Mein Anforderungsbenutzer sollte irgendetwas in Ihrer benutzerdefinierten 404-Seite anzeigen. Ich dachte, es ist sehr einfach. Aber Sie sollten die Handhabung von 404 richtig verstehen:

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

Ich fand diesen Artikel sehr hilfreich. Sollte sofort gelesen werden. Custome error page-Ben Foster

0
Thakur Rock