Frage ASP.NET MVC Url.Action fügt der generierten URL aktuelle Routenwerte hinzu


Ich habe diese Frage ein paar Mal hier in SO gesehen, aber keine von ihnen mit einer akzeptablen Antwort:

ASP.NET MVC @ Url.Action enthält aktuelle Routendaten
ASP.NET MVC fügt Routenwerte implizit hinzu

Grundsätzlich habe ich Controller mit einer Aktionsmethode namens Group, es hat eine Überladung, die keine Parameter empfängt und eine Liste von Elementen anzeigt und eine andere, die eine ID erhält und Details für diese Gruppe anzeigt.

Wenn ich so etwas tue:

Url.Action("Group", "Groups");

Von der Hauptseite der Site (/) gibt es eine URL wie folgt zurück:

"mysite.com/Groups/Group"

Das ist in Ordnung Nun, wenn die aktuelle Adresse der Site / Groups / Group / 1 ist Und ich rufe dieselbe Methode an

Url.Action("Group", "Groups");

Die zurückgegebene URL lautet wie folgt:

"mysite.com/Groups/Group/1"

Es fügt automatisch den Wert der Route für die aktuelle Seite hinzu, wenn die URL generiert wird. Auch wenn ich die URL so erzeuge:

Url.Action("Group", "Groups", null);

Damit explizit angegeben wird, dass keine Routenwerte gewünscht werden, ist die generierte URL identisch. Um die gewünschte Adresse zu bekommen, muss ich den Routenwert explizit auf eine leere Zeichenfolge setzen, so:

Url.Action("Group", "Groups", new {id=""});

Dies erzeugt die folgende URL:

"mysite.com/Groups/Group"

Meine Frage ist, warum passiert das? Wenn ich keine Routenwerte festlege, sollten sie diese nicht zur generierten URL hinzufügen.


51
2017-08-20 16:52


Ursprung


Antworten:


Url.Action wird die aktuellen Anforderungsparameter erneut verwenden, wenn Sie sie nicht explizit festlegen. Es ist standardmäßig im Outbound-URL-Matching-Algorithmus. Bei der Suche nach den Routendatenparametern in einem Prozess zum Erzeugen einer URL werden Parameter aus folgenden Quellen genommen:

1) explizit angegebene Werte

2) Werte aus der aktuellen Anfrage

3) Standardeinstellungen

In der Reihenfolge, die ich oben angegeben habe.

Der Outbound-Matching-Algorithmus für Routen ist kompliziert. Daher sollten Sie alle Parameter für die Anfrage explizit festlegen, wie in Ihrem Beispiel


49
2017-08-20 17:07



Meine Anwendung legt explizit Routenwerte fest und möchte keinen magischen Wert aus der aktuellen Anfrage. Ich möchte die volle Kontrolle haben.

Ich habe eine Erweiterung gemacht, die mit meiner Route-Bibliothek-Sammlung koexistiert. Daher der einzelne RouteValueDictionary-Parameter. (Siehe meinen Kommentar zur Routenbibliothek unten) 

Hier entferne ich irgendwelche Routenwerte von der Anfrage, bevor eine URL erzeugt wird.

(Hinweis: Für das Feld array.contains issuecase siehe: Wie kann ich Array.Contains machen, bei dem die Groß- und Kleinschreibung in einem String-Array nicht beachtet wird?)

public static string Action(this UrlHelper helper, 
                            RouteValueDictionary routeValues)
{
    RemoveRoutes(helper.RequestContext.RouteData.Values);

    string url = helper.Action(routeValues["Action"].ToString(), routeValues);
    return url;
}

public static void RemoveRoutes(RouteValueDictionary currentRouteData)
{
    List<string> keyList = new List<string>(currentRouteData.Keys);

    string[] ignore = new[] { "Area", "Controller", "Action" };
    foreach (string key in keyList)
    {
        if (!ignore.Contains(key, StringComparer.CurrentCultureIgnoreCase))
            currentRouteData.Remove(key);
    }
}

Ich habe Form- und ActionLink-Erweiterungsmethoden, die die RemoveRoutes-Methode verwenden. Kein Helfer in meiner MVC-Bibliothek verwendet eine Methode, die keine von mir erstellte Erweiterungsmethode ist. Dadurch werden alle gerouteten Daten bereinigt, bevor URLs generiert werden.

Als Referenz verwende ich AttributeRouting. Hier ist ein Beispiel für eine Route aus meiner Routenbibliothek.

public static RouteValueDictionary DisplayNews(int newsId)
{
    RouteValueDictionary route = new RouteValueDictionary();
    route["Area"] = _area;
    route["Controller"] = _controller;
    route["Action"] = "DisplayNews";
    route["newsId"] = newsId;
    return route;
}

2
2018-04-02 01:52



Als ich die Antwort von objectbox gelesen habe, dachte ich, dass ich mehrere Links in meinem Code ändern müsste. Ich habe dann versucht, eine Standardroute hinzuzufügen, ohne die Standardparameter zu verwenden, die das Problem gelöst haben:

routes.MapRoute(
    "ArtistArtworkDefPage",
    "Artist/{username}/Artwork",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    }
);

routes.MapRoute(
    "ArtistArtwork",
    "Artist/{username}/Artwork/{page}",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    },
    new { page = @"\d+" }
);

2
2018-04-28 14:24



Einfaches Beispiel:

public class ProductController : Controller
{
  public ActionResult Edit(int id)
  {
    return View();
  }

  [Route("Product/Detail/{id:int}")]
  public ActionResult Detail(int id)
  {
    return View();
  }
}

Die Bearbeitungsansicht enthält nur Folgendes:

@{ Layout = null;}
@Url.Action("Detail", "Cmr")

Wenn Sie also Ihre Site z. localhost:randomPort/Product/Edit/123Sie erhalten die nächste Antwort: /Product/Detail/123

Warum? weil Route Attribut hat Parameter benötigt id. ID-Parameter wird von URL gelesen, obwohl wir nur geschrieben haben Url.Action(methodName, controller) - ohne Parameter anzugeben. Es macht auch keinen Sinn, ein Methodendetail ohne ID zu haben.

Damit Attribute funktionieren, muss die nächste Zeile hinzugefügt werden RouteConfig.cs:

public static void RegisterRoutes(RouteCollection routes)
{
  ...
  routes.MapMvcAttributeRoutes();
  ...
}

1
2018-03-10 10:27



Hier ist eine Problemumgehung, die weder das Attributrouting erfordert noch die aktuellen Routenwerte in Ihrer Anfrage ändert. Diese Idee kommt von http://notherdev.blogspot.ca/2013/10/aspnet-mvc-current-values-used-by-url-action.html, die ich leicht geändert habe, um ohne MvcFutures zu arbeiten.

Ich habe mein eigenes gebaut Action Erweiterung auf UrlHelper mit einem zusätzlichen Bool, mit dem Sie optional die aktuellen Routenwerte ignorieren können (um nicht mit den bestehenden Aktionsmethoden auf UrlHelper zu kollidieren).

Was es tut, baut einen komplett neuen RequestContext aus dem aktuellen, unter Verwendung der aktuellen Route aber nicht die aktuellen Routenwerte. Dann geben wir das an die Basishelfer weiter. Auf diese Weise werden die Basis-Helfer, wenn sie nachsehen, was die Routenwerte aus dem Anforderungskontext sind, keine finden und sie daher nicht verwenden, wenn sie die URL erzeugen.

public static string Action(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, bool ignoreCurrentRouteValues=false) {
    var routeValueDictionary = new RouteValueDictionary(routeValues);
    var requestContext = urlHelper.RequestContext;
    if (ignoreCurrentRouteValues) {
        var currentRouteData = requestContext.RouteData;
        var newRouteData = new RouteData(currentRouteData.Route, currentRouteData.RouteHandler);
        requestContext = new RequestContext(requestContext.HttpContext, newRouteData);
    }

    return UrlHelper.GenerateUrl(null, actionName, controllerName, routeValueDictionary,
        urlHelper.RouteCollection, requestContext, includeImplicitMvcValues: false);
}

1
2017-12-01 18:04



Ein einfacher Workaround wäre der erste Aufruf

Url.Action("dummy", new { ... }) 

und benennen Sie Dummy dann in der resultierenden Zeichenfolge in den richtigen Aktionsnamen um.


0
2017-08-18 08:54



Eine alberne Problemumgehung, die ich gefunden habe, die funktioniert, um die aktuellen Seitenroutingdaten zu verarbeiten, einschließlich das Ändern des Seitenparameters auf Ihren Einstellungen

@{
    var current_route_1 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);
    var current_route_2 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);

    //If you want to customize the routing values
    current_route_1["controller"] = "Controller1";
    current_route_2["controller"] = "Controller2";
}

@Url.RouteUrl(current_route_1);
@Url.RouteUrl(current_route_2);

0
2018-04-28 07:23