ASP.NET MVC - Alle Routen und Standardrouten abfangen

72

Bei dem Versuch, meine Anwendung dazu zu bringen, 404-Fehler korrekt zu erzeugen, habe ich am Ende meiner Routentabelle eine Catch-All-Route implementiert, wie unten gezeigt:

 routes.MapRoute(
            "NotFound", _
           "{*url}", _
           New With {.controller = "Error", .action = "PageNotFound"} _
       )

Damit dies funktioniert, musste ich jedoch die Standardroute entfernen:

{controller}/action/{id}

Nachdem die Standardeinstellung entfernt wurde, funktionieren die meisten meiner Aktionslinks nicht mehr. Die einzige Möglichkeit, sie wieder zum Laufen zu bringen, besteht darin, für jeden Controller / jede Aktion individuelle Routen hinzuzufügen.

Gibt es eine einfachere Möglichkeit, als für jeden Controller / jede Aktion eine Route hinzuzufügen?

Ist es möglich, eine Standardroute zu erstellen, die es weiterhin ermöglicht, alle Routen zu erfassen, wenn der Benutzer versucht, zu einer unbekannten Route zu navigieren?

Sean Taylor
quelle
1
Warum hat 404 Ihrer Meinung nach nicht richtig funktioniert?
Quakkels
@quakkels, ich hatte die gleiche Beschwerde wie @Sean. ASP.NET löst eine 404 aus, führt dann aber eine 302-Umleitung durch, sodass das Ergebnis keine echte 404 auf der betreffenden Seite ist. Dies ist bei der Verwendung customErrors.
Dustin Laine
2
IMO, wenn ich dorthin gehe, domain.com/nopagewo nopage kein gültiger Controller ist, sollte es einen 404 geben.
Dustin Laine
4
stimme voll und ganz zu, 404 = Nicht gefunden, egal ob es sich um eine physische Datei handelt oder nicht
Sean Taylor
2
HTTP 404 - "Die angeforderte Ressource wurde nicht gefunden." Bei HTTP / REST dreht sich alles um Ressourcen und es stellt sich die Frage, was eine 'Datei' ist.
Luke Puplett

Antworten:

108

Verwenden Sie Routenbeschränkungen

In Ihrem Fall sollten Sie Ihre Standardroute definieren {controller}/{action}/{id}und diese einschränken. Wahrscheinlich im Zusammenhang mit Controllernamen oder vielleicht sogar Aktionen. Dann setzen Sie den Haken alle nach und es sollte gut funktionieren.

Wenn also jemand eine Ressource anfordert, die eine Einschränkung nicht erfüllt, stimmt die Catch-All-Route mit der Anforderung überein.

Damit. Definieren Sie zuerst Ihre Standardroute mit Routenbeschränkungen und anschließend die Route "Alle fangen":

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { controller = "Home|Settings|General|..." } // this is basically a regular expression
);
routes.MapRoute(
    "NotFound",
    "{*url}",
    new { controller = "Error", action = "PageNotFound" }
);
Robert Koritnik
quelle
Guter Punkt. Ich dachte, die erste Route würde nur übereinstimmen, wenn der Controller-Name übereinstimmt ... Danke!
Andrei Rînea
@andreirinea nein, die Routing-Karte überprüft keine Typen. Es wird nur geprüft, ob die URL der aktuellen Anforderung mit der Zuordnung übereinstimmt. Das einzige, was jede Route überprüft, sind Einschränkungen, wenn vorhanden.
Robert Koritnik
15
Sie können Reflection verwenden, um Ihre Controller-Einschränkung zu erstellen und so jedes Mal, wenn Sie einen neuen Controller hinzufügen, einen Wartungsaufwand zu vermeiden. private static string GetAllControllersAsRegex() { var controllers = typeof(MvcApplication).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller))); var controllerNames = controllers.Select(c => c.Name.Replace("Controller", "")); return string.Format("({0})", string.Join("|", controllerNames)); }
Blisco
1
Ist dies der richtige Weg, um 404-Fehler in URLs zu implementieren, die nicht nach Route behoben werden? Oder gibt es nur eine Problemumgehung und eine sauberere Lösung? Ich finde es lustig, dass man 2015 manuell etwas erledigen muss, was vom Server erledigt werden sollte.
f470071
1
@ f470071: Du hast recht. Unter normalen Bedingungen kann dies leicht vom Webserver gehandhabt werden, aber manchmal haben Anwendungen bestimmte Einschränkungen, die eine willkürliche Ressourcenabfrage ermöglichen sollten. Das einzig Seltsame, was OP tun möchte, ist, Fehler in seiner App zu behandeln, während sie dies mit den üblichen Webserverfunktionen tun könnten. Ich würde auch die Catch-All-Routendefinition am Ende weglassen und nur das eingeschränkte Routing verwenden, wie in meiner Antwort beschrieben.
Robert Koritnik
22
//this catches all  requests
routes.MapRoute(
    "Error",
    "{*.}",
     new { controller = "PublicDisplay", action = "Error404" } 
);

Fügen Sie diese Route am Ende der Routentabelle hinzu

Praveen Prasad
quelle
5
Dies fängt sicherlich alle Anfragen an. Wenn Sie es jedoch vor der Standardroute deklarieren, wird die Standardroute überschrieben. Wenn Sie es nach der Standardroute deklarieren, wird es nicht aufgerufen, da die Standardroute die Anforderung abfängt.
Tobias
2
"{*.*}",arbeitete für mich auf asp.net vb als catchall
bendecko
"{*.}"Ich habe nur Routen ohne Verlängerung gefangen. Geändert, "{*.*}"um alle Routen einschließlich .aspx zu erfassen.
Keith Sirmons
7

Ah, das Problem ist, dass Ihre Standardroute alle 3 Segment-URLs abfängt . Das Problem hierbei ist, dass das Routing lange bevor wir bestimmen, wer die Anforderung bearbeiten wird, ausgeführt wird. Daher stimmt jede URL mit drei Segmenten mit der Standardroute überein, auch wenn später festgestellt wird, dass kein Controller dafür zuständig ist.

Sie können auf Ihrem Controller die HandleMissingAction-Methode überschreiben. Sie sollten das Tag auch verwenden, um alle 404 Probleme zu erfassen.

Gehackt
quelle
2

Nun, ich habe festgestellt, dass es keinen guten Weg gibt, dies zu tun. Ich habe die redirectModeEigenschaft des customErrorsauf gesetzt ResponseRewrite.

<customErrors mode="On" defaultRedirect="~/Shared/Error" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="~/Shared/PageNotFound"/>
</customErrors>

Dies gibt mir das gewünschte Verhalten, zeigt aber nicht die formatierte Seite an.

Für mich ist das schlecht gemacht, was SEO angeht. Ich habe jedoch das Gefühl, dass es eine Lösung gibt, die mir fehlt, da SO genau das tut, was ich möchte. Die URL bleibt auf der fehlgeschlagenen Seite und löst eine 404 aus. Überprüfen Sie stackoverflow.com/fail in Firebug.

Dustin Laine
quelle
Es ist komisch, dass Sie sagen sollten, als ich mich zum ersten Mal mit dem Problem befasste, habe ich zuerst überprüft, wie SO damit umgeht. Ich könnte Routen für jede Aktion manuell hinzufügen, aber ich denke, dies wäre im Laufe der Zeit schwer zu pflegen. Ich denke wirklich, dass MS eine bessere Anleitung zu diesem Thema geben könnte.
Sean Taylor
3
Vielleicht, wenn wir Jeff nett fragen, wird er es mit uns teilen? :)
Sean Taylor
Nachdem ich viel herumgegraben und verschiedene Lösungen ausprobiert habe, scheint diese für mich in Ordnung zu sein. stackoverflow.com/questions/619895/…
Sean Taylor
1

Ich würde dies als die am besten lesbare Version empfehlen. Sie benötigen dies in Ihrer RouteConfig.cs und einem Controller namens ErrorController.cs, der eine Aktion 'PageNotFound' enthält. Dies kann eine Ansicht zurückgeben. Erstellen Sie eine PageNotFound.cshtml, die als Antwort auf die 404 zurückgegeben wird:

        routes.MapRoute(
            name:  "PageNotFound",
            url:  "{*url}",
            defaults: new { controller = "Error", action = "PageNotFound" }
        );

Wie man das liest:

name: "PageNotFound"

= Erstelle eine neue Routenvorlage mit dem beliebigen Namen 'PageNotFound'

url:"{*url}"

= Verwenden Sie diese Vorlage, um alle ansonsten nicht behandelten Routen zuzuordnen

defaults: new { controller = "Error", action = "PageNotFound" }

= Definieren Sie die Aktion, der ein falscher Pfad zugeordnet wird (die Aktionsmethode 'PageNotFound' im Fehlercontroller). Dies ist erforderlich, da ein falsch eingegebener Pfad offensichtlich keiner Aktionsmethode zugeordnet werden kann

Chris Halcrow
quelle
0

Sie sind wahrscheinlich besser dran, das 404-Fehlerdokument im Abschnitt der Konfiguration zu konfigurieren, als die Standardroute wiederherzustellen.

FWIW, ich denke, die Standardroutenanforderung ist ebenfalls verzögert.

Wyatt Barnett
quelle
0

Meine Lösung besteht aus 2 Schritten.

Ich habe dieses Problem ursprünglich gelöst, indem ich diese Funktion zu meiner Datei Global.asax.cs hinzugefügt habe:

protected void Application_Error(Object sender, EventArgs e)

Wo ich versucht habe, Server.GetLastError () in eine HttpException umzuwandeln, und dann GetHttpCode überprüft habe. Diese Lösung wird hier detailliert beschrieben:

Benutzerdefinierte ASP.NET MVC-Fehlerbehandlung Application_Error Global.asax?

Dies ist nicht die ursprüngliche Quelle, aus der ich den Code erhalten habe. Dies fängt jedoch nur 404 Fehler ab, die bereits weitergeleitet wurden. In meinem Fall ist dies eine beliebige URL mit zwei Ebenen.

Diese URLs würden beispielsweise die 404-Seite anzeigen:

www.site.com/blah

www.site.com/blah/blah

www.site.com/blah/blah/blah würde jedoch einfach sagen, dass die Seite nicht gefunden werden konnte. Das Hinzufügen Ihres Fangs alle Route NACH all meinen anderen Routen löste dies:

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

Die NotFound-Route scheint jedoch keine Anforderungen mit Dateierweiterungen weiterzuleiten. Dies funktioniert, wenn sie auf verschiedenen Routen erfasst werden.

Marcus10110
quelle