ASP.NET MVC Url.Akcja dodaje bieżące wartości trasy do wygenerowanego adresu url

Widziałem to pytanie kilka razy tutaj w SO, ale żadne z nich nie ma żadnej akceptowalnej odpowiedzi:

ASP.NET MVC @ Url.Akcja zawiera aktualne dane o trasie
ASP.NET MVC domyślnie dodaje wartości trasy

W zasadzie mam kontroler z metodą akcji o nazwie Group, ma przeciążenie, które nie odbiera żadnych parametrów i wyświetla listę elementów oraz inny, który otrzymuje id i wyświetla szczegóły dla tej grupy.

Jeśli Robię coś takiego:

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

Ze strony głównej witryny ( / ) zwraca adres url w następujący sposób:

"mysite.com/Groups/Group"

Co jest w porządku Teraz, jeśli aktualny adres strony to / Groups / Group/1 And I call the same method

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

Zwracany adres url jest następujący:

"mysite.com/Groups/Group/1"

Automatycznie dodaje wartość trasy dla bieżącej strony podczas generowania adresu URL. Nawet jeśli wygeneruję adres URL w ten sposób:

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

W ten sposób wyraźnie określając, że nie chcę żadnej trasy wartości, wygenerowany adres URL jest taki sam. Aby uzyskać żądany adres, muszę jawnie ustawić wartość trasy na pusty łańcuch, tak:

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

To wygeneruje następujący adres url:

"mysite.com/Groups/Group"
Moje pytanie brzmi, dlaczego tak się dzieje? Jeśli nie ustawiam żadnych wartości trasy, nie powinno się dodawać ich do wygenerowanego adresu URL.
Author: Community, 2011-08-20

7 answers

Url.Action użyje ponownie bieżących parametrów żądania, jeśli nie zostaną one jawnie ustawione. Jest to algorytm dopasowujący adres URL wychodzący. Podczas wyszukiwania parametrów danych trasy w procesie generowania adresu url, parametry są pobierane z:

1) jawnie podane wartości

2) wartości z bieżącego żądania

3) defaults

W kolejności podanej powyżej.

Algorytm dopasowania wychodzącego dla tras jest skomplikowany, więc dobrą praktyką jest jawnie ustaw wszystkie parametry dla request, tak jak w przykładzie

 50
Author: objectbox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-20 17:20:15

Moja aplikacja jawnie ustawia wartości trasy i nie chce magicznej wartości z bieżącego żądania. Chcę mieć pełną kontrolę.

Zrobiłem rozszerzenie, które współgra z moją kolekcją route library. stąd pojedynczy param. (Zobacz komentarz mojej biblioteki tras na dole)

Tutaj usuwam wszelkie routevalues z żądania przed wygenerowaniem adresu url.

(uwaga: dla tablicy.zawiera część ignorecase, patrz: Jak mogę zrobić / Align = "left" / Zawiera wielkość liter w tablicy łańcuchów?)

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

Mam metody rozszerzenia Form i ActionLink, które używają metody RemoveRoutes. Żaden helper w mojej bibliotece mvc nie używa metody, która nie jest stworzoną przeze mnie metodą rozszerzenia. W ten sposób wszystkie routedata są czyszczone przed wygenerowaniem adresów URL.

w celach informacyjnych używam AttributeRouting. Oto przykład jednej trasy z mojej biblioteki tras.

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
Author: Valamas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37

Kiedy przeczytałem odpowiedź objectbox, pomyślałem, że będę musiał zmodyfikować kilka linków w moim kodzie. Następnie próbowałem dodać domyślną trasę pomijając domyślne parametry, które rozwiązały problem:

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
Author: tmorell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-28 14:24:30

Prosty przykład:

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

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

Widok edycji zawiera tylko to:

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

Więc po uruchomieniu strony np. localhost:randomPort/Product/Edit/123 otrzymujesz następną odpowiedź: /Product/Detail/123

Dlaczego? Ponieważ Route atrybut posiada wymagany parametr id. Parametr Id jest odczytywany z adresu url, chociaż napisaliśmy tylko Url.Action(methodName, controller) - bez podania parametru. Również nie ma sensu mieć szczegóły metody bez id.

Aby atrybuty działały, należy dodać do RouteConfig.cs następny wiersz:

public static void RegisterRoutes(RouteCollection routes)
{
  ...
  routes.MapMvcAttributeRoutes();
  ...
}
 2
Author: broadband,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-10 10:27:42

Głupie obejście znalazłem, że działa do obsługi bieżącej strony routingu danych, w tym zmiana parametru strony według preferencji

@{
    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);
 1
Author: Mahdi Fathalla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-28 07:23:22

Oto obejście, które nie wymaga trasowania atrybutów ani nie zmienia bieżących wartości trasy w żądaniu. Ten pomysł pochodzi z http://notherdev.blogspot.ca/2013/10/aspnet-mvc-current-values-used-by-url-action.html , który zmieniłem nieco, aby działał bez MvcFutures.

Zbudowałem własne rozszerzenie Action Na UrlHelper z dodatkowym boolem, który pozwala opcjonalnie ignorować bieżące wartości trasy (aby nie kolidować z istniejącymi metodami akcji na UrlHelper.)

To, co robi, to buduje zupełnie nowy RequestContext z bieżącego, używając bieżącej trasy, ale nie bieżące wartości trasy. Potem przekażemy to pomocnikom bazy. W ten sposób, gdy podstawowe helpery przejdą do sprawdzenia, jakie są wartości trasy z kontekstu żądania, nie znajdą żadnych, a tym samym nie użyją ich podczas generowania adresu URL.

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
Author: Ber'Zophus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-01 18:04:48

Proste obejście będzie pierwszym wywołaniem

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

A następnie zmień nazwę dummy w wynikowym łańcuchu na poprawną nazwę akcji.

 0
Author: Martin Staufcik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 08:54:02