Laden...

Name Controller vor Ausführung / Aufruf in Filter oder Handler ändern

Erstellt von mosspower vor einem Jahr Letzter Beitrag vor einem Jahr 696 Views
mosspower Themenstarter:in
456 Beiträge seit 2007
vor einem Jahr
Name Controller vor Ausführung / Aufruf in Filter oder Handler ändern

Hallo,

ich erstelle gerade ein API-Projekt und möchte, so wie es eigentlich auch sein soll, nur .NET (Core) verwenden, ohne .NET.ASP (Core).

Jetzt habe ich eine "Legacy-ASP.NET-Anwendung" laufen, die sowohl als Webanwendung als auch als API dient.
Bei dieser konnte ich vor Jahren einen Request in einem Handler abfangen vor der Verarbeitung und die Controller- und Actionnamen anpassen, wenn nötig, z.B. so ...


/// <summary>
  /// Class for enabling hyphanated controller and action names
  /// </summary>
  public class HyphenatedRouteHandler : MvcRouteHandler {
    /// <summary>
    /// <see cref="MvcRouteHandler.GetHttpHandler(RequestContext)"/>
    /// </summary>
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext) {
      requestContext.RouteData.Values["controller"] = requestContext.RouteData.Values["controller"].ToString().Replace("-", "").ToLower();
      requestContext.RouteData.Values["action"] = requestContext.RouteData.Values["action"].ToString().Replace("-", "").ToLower();
      return base.GetHttpHandler(requestContext);
    }

Hier kann man dann "saubere" Snake-Case-Urls verwenden, z.B. /cart-service/next-step und diese Mappen dann auf den Controller CartServcieController mit der dazugehörigen Action NextStep. (Bitte keine Diskussion, das ist nur ein Beispiel und ich würde das hier natürlich /cart/next nennen in produktiv) ...

Jetzt habe ich bei meiner neuen API ein ähnliches Problem, ich würde gerne an jedem Controller einen Postfix anhängen, finde aber nach Stunden der Suche im Web keine Hilfe.
Kann mir ggf. hier jemand helfen - es sollte doch jetzt immer noch möglich sein, oder?


 // Add routing
      app.MapControllerRoute(
        name: "api",
        pattern: "api/{culture}/{controller}/{action}/{ids?}",
        constraints: new { culture = @"[a-z]{2}" });

Hier hatte ich schon einen EndpointFilter hinzugefügt, aber leider sind hier die Controller/Action-Namen readonly und kann sie nicht verändern, bzw. komme gar nicht an das nötige Objekt ran, da es geschützt (internal) ist.

16.834 Beiträge seit 2008
vor einem Jahr

ich erstelle gerade ein API-Projekt und möchte, so wie es eigentlich auch sein soll, nur .NET (Core) verwenden, ohne .NET.ASP (Core).

Was soll dieser Satz heissen? Was heisst "wie es sein soll" ohne ".NET.ASP" (was es nicht gibt).

Hier kann man dann "saubere" Snake-Case-Urls verwenden, z.B. /cart-service/next-step und diese Mappen dann auf den Controller CartServcieController mit der dazugehörigen Action NextStep.(Bitte keine Diskussion, das ist nur ein Beispiel und ich würde das hier natürlich /cart/next nennen in produktiv)

Du bist in nem offenen Forum, dann musst auch Antworten ertragen können 😉
Sowas selbst zu designen war damals schon ein fischiger Weg, da die Empfehlung hier (seit ~15 Jahren) die Route Attribute sind, was auch noch heute seine Gültigkeit hat.

Jetzt habe ich bei meiner neuen API ein ähnliches Problem, ich würde gerne an jedem Controller einen Postfix anhängen, finde aber nach Stunden der Suche im Web keine Hilfe.

Erklär nicht nur, woran Du gescheitert bist, sondern was der Zweck ist. Dann kann man Dir das mit potentiellen Lösungen mit deren Vor- und Nachteilen erklären.
Aber auch hier: Route Attribute sind für alle expliziten Szenarien Wahl No 1.

mosspower Themenstarter:in
456 Beiträge seit 2007
vor einem Jahr

Hallo Abt,

ich meinte natürlich ASP.NET ("Core"), aber ich denke, das wusstest Du selber?

Du schreibst, dass dafür seit ~15 Jahren die "Empfehlung" Route Attribute sind.
Nun, das ist mir bewusst, und mit Verlaub, das finde ich einen richtigen Schwachsinn.
Ich denke nur an hunderten von Controllerklassen mit tausenden Actionmethoden mit jeweils einem Attribute mit String gekennzeichnet, da wird mir schlecht. In Verbindung mit ApiController-Attribute ist dieser Schwachsinn sogar "mandatory" 😉 -

Fehlermeldung:
System.InvalidOperationException: 'Action 'ASP.NET_Core_Web_API.Filter.ActionFilter.OnActionExecuting (ASP.NET Core Web API)' does not have an attribute route. Action methods on controllers annotated with ApiControllerAttribute must be attribute routed.'

das ist für mich ein Steinzeitrückschritt aus Redmond, solche Empfehlungen abzugeben, aber das ist ein anderes Thema und natürlich Ansichtssache.

Ich glaube, da ist auch aktuell ein Feature in Arbeit, dies zentral verwalten zu können über Routingeinstellungen, wie man es auch erwarten würde.

Aber danke für Deine Hinweise. Ich hake das mal ab .... ich kann damit leben, dass es nicht (mehr) geht, zwar nicht gut, aber ich muss wohl 😉

16.834 Beiträge seit 2008
vor einem Jahr

ich meinte natürlich ASP.NET ("Core"), aber ich denke, das wusstest Du selber?

Du schreibst explizit

nur .NET (Core) verwenden, ohne .NET.ASP (Core).

was sich so liest, dass Du eine API mit .NET Core bauen willst OHNE ASP.NET Core als Framework zu verwenden.

das ist für mich ein Steinzeitrückschritt aus Redmond,

Tjo, dann scheinst das .NET Ökosystem nicht ganz so verstanden zu haben, denn maßgeblich kommen die ASP.NET Core Elemente aus der Community, geführt "aus Redmond".
Deine Haltung ist im Endeffekt gegen die Ansicht und Empfehlung von denen (nicht nur Microsoft), die ASP.NET Core / moderne Webframeworks entwickeln.

Wenn man sich die Art und Weise, wie Routing funktionier, einfach mal anschaut, dann sollte man in der Lage sein, dass man das verstehen kann.
Es ist technisch und aus Architektursicht absolut sinnvoll und sogar der beste Weg. Und das ist nicht mal ASP.NET-only, sondern findet man in eigentlich jedem modernen Backend- wie Frontend Web Framework mit Routing Komponenten, da es entkoppelnde Architekturprinzipien folgt.

Ich denke nur an hunderten von Controllerklassen mit tausenden Actionmethoden mit jeweils einem Attribute mit String gekennzeichnet, da wird mir schlecht. In Verbindung mit ApiController-Attribute ist dieser Schwachsinn sogar "mandatory" -

Du verfolgst offenbar eine reine implizite Struktur (conventional routing), das in eigentlich allen Web-Framework als durchaus problematisch angesehen wird (Performance-Impact, Stabilität, Erweiterbarkeit) und schließt auch Folgestrukturen (zB ein SDK) aus, was deswegen auch aus fast alle Web Frameworks mittlerweile raus geflogen ist.
Man kann Route-Attributes sehr effizient ohne Magic Strings umsetzen und sich gleichzeitig damit eine Routing-Komponente bauen, damit man so Quatsch wie "RouteToAction" nicht mehr braucht, die höchst ineffizient und fehleranfällig sind.
Alles Gründe, weswegen das Attribut (zurecht!) mandatory wurde.

Man kann auch die Minimal APIs verwenden (wenn man paar Features nicht braucht, die derzeit MVC-Exlusiv sind), aber auch hier ist die harte Angabe des Pfads der empfohlene Weg (MapXYZ mit Handlern aka Static Method).

Ich glaube, da ist auch aktuell ein Feature in Arbeit, dies zentral verwalten zu können über Routingeinstellungen, wie man es auch erwarten würde.

Nichts davon gehört und sehe auch nichts in den Issues oder Roadmaps dazu.
Würde auch dem Wunsch, dass Routing eben modularer wird, ist und bleibt, widersprechen. Gibt, so wie es aussieht, keinerlei Changes beim MVC Routing.
Die einzige Alternative mit sowohl zentralen wie auch modularen Möglichkeiten, ist die Minimal API.

mosspower Themenstarter:in
456 Beiträge seit 2007
vor einem Jahr

... was sich so liest, dass Du eine API mit .NET Core bauen willst OHNE ASP.NET Core als Framework zu verwenden.

Naja, wenn ich eine reine REST-API bauen möchte, dann sollte eigentlich auch .NET ("Core") ausreichen - so zumindest meine "Denke"
Hier Core in Anführungszeichen, weil es das "eigentlich" ab Version .NET 5 nicht mehr gibt (nein, kein Durcheinander aus Redmond mit .NET/FRAMEWORK, Standard, Core. Web API 2 usw.) 😉 ...

Man kann auch die Minimal APIs verwenden (wenn man paar Features nicht braucht, die derzeit MVC-Exlusiv sind), aber auch hier ist die harte Angabe des Pfads der empfohlene Weg ...

Warum soll man MVC-Inklusiv verwenden, wenn man "nur" eine REST-API erstellen möchte. Das muss mir mal einer erklären - ich verliere da echt oft den Überblick oder auch das Verständnis. Zumindest ist es ein legitimer Weg, von ControllerBase abzuleiten und dann konventionelles Routing weiterhin zu verwenden ohne den ganzen MVC-Overhead zu haben. (bezieht sich jetzt auf ControllerBase und nicht die Route Attributes)

Naja, ich machs mal kurz ... zumindest macht man sich diesbezüglich "ernsthaft" Gedanken - müssen da aber bis zur Version 8 warten denke ich.
https://github.com/dotnet/aspnetcore/issues/43192

[Edit]
.... ich verstehe aber nun langsam immer mehr, warum die Attribute bei nachvollziehbaren und (mitunter) sehr komplexen Routes dann doch der einfachere und höchstwahrscheinlich auch performantere Weg sind, auch wenn ich mich damit nicht so richtig anfreunden möchte, aber ich glaub, ich nehme Attribute, soz. als das kleinere Übel.
Danke für die Hinweise, damit erledigen sich nun viele andere Probleme.
[/Edit]

16.834 Beiträge seit 2008
vor einem Jahr

Warum soll man MVC-Inklusiv verwenden, wenn man "nur" eine REST-API erstellen möchte.

Du baust offenbar eine HTTP API, keine REST API 😉
REST ist ein Paradigma, kein Implementierungsstandard. Es ist ein sehr streng standardisierter Regel-Aufsatz auf einer HTTP API (JSON/XML/...). REST ist eine Daten-strukturierte Regelsammlung, nicht Aktionen.
Basics: https://dotnet.rest

ASP.NET Core bietet mehrere Möglichkeiten Endpunkte umzusetzen, wobei MVC das mächtigste ist.
Minimal API ist eben sehr hart fokussiert für APIs, hat sehr wenig default overhead, bietet sehr viel funktionale Möglichkeiten - aber eben für nix anderes als API derzeit. MVC deckt mehr Zwecke ab, als nur APIs.

Keine Ahnung, wie Du da plötzlich REST ein vermischst. Hat damit null zutun, eben weil REST auch einfach nur ein Paradigma ist, und keine Implementierung.

(Bitte keine Diskussion, das ist nur ein Beispiel und ich würde das hier natürlich /cart/next nennen in produktiv)

Hinweis dazu: "/cart/next" sind URI-Formate, die REST Standards nicht einhalten. "/cart/next" ist ganz offenbar nicht Datengetrieben, sondern eine Aktion: daher kein REST.
Aber das ist nicht schlimm. REST ist nicht "besser" oder "schlechter" als eine HTTP API.
Das ist also nur die falsche Bezeichnung - was leider oft passiert.

Zumindest ist es ein legitimer Weg, von ControllerBase abzuleiten und dann konventionelles Routing weiterhin zu verwenden ohne den ganzen MVC-Overhead zu haben. ControllerBase ist die absolute Basis von MVC. Du kannst quasi kein MVC machen ohne ControllerBase.
MVC ist extremst mächtig und enorm optimiert, aber hat einen gewissen Overhead, der notwendig ist, dass MVC effizient ist und in Architekturen passt.
MVC hat aber auch Alternativen wie Razor Pages oder eben seit einiger Zeit Minimal APIs. Letztere sind enorm optimiert für APIs und moderne Architekturanforderungen an API Endpunkte.

Konventionelles Routing hat aber absolut nirgends mehr Platz. Es ist ein leider "einfaches" Umsetzungsmodell, mit enormen Risiken und Nachteilen.

müssen da aber bis zur Version 8 warten denke ich.

>

Das steht da nicht.
Brennan Conroy hat geantwortet, dass sie schauen, was potentiell möglich ist und es einfach mal ins Planning verschoben.
Da steht nirgends, dass das mit .NET 8 kommen würde.

Würde es mit .NET 8 kommen, dann hätte es den "8.0" Tag und nicht "NET 8 Planning". Der Planning Milestone wird nach jedem Release umbenannt. Wenn also 8.0 Released wird, dann gibt es ein "9.0" Tag für das nächste Release und "NET 8 Planning" wird umbenannt in "NET 9 Planning".
Im Planning werden einfach alle alle Anfrage gesammelt ohne dass das eine fixe Aussage wäre, dass das jemals kommen wird. Nur der WIP Issue trackt und zeigt die Roadmap, nichts anderes. Für ASP.NET Core 8: [WIP] ASP.NET Core Roadmap for .NET 8 · Issue #44984 · dotnet/aspnetcore

Und auch die Antwort, dass man damit Route Prefix vereinfachen könnte: völlig falsche Baustelle.
Braucht man alles nicht, wenn man den Magic String Quatsch lassen würde, und sich einfach Routes Templates baut.


public static class MyRoutes
{
    public const string ApiRoot = "api";

    public static class Users
    {
            public const string Users = $"{ApiRoot}/users";
            public const string UserAdd = $"{Users}";
    }
}

[Route(MyRoutes.Users.UserAdd)]
[HttpPost]
public ActionResult AddUser(....

Aber das ist ein klassiches Versäumnis des Devs, nicht des Frameworks.