ASP.NET MVC Url.Action добавляет текущие значения маршрута в сгенерированный URL-адрес
Я видел этот вопрос пару раз здесь, в SO, но ни один из них с приемлемым ответом:
ASP.NET MVC @Url.Action включает в себя текущие данные маршрута
ASP.NET MVC неявно добавляет значения маршрута
В основном у меня есть Controller с методом действия Group, он имеет перегрузку, которая не получает никаких параметров и отображает список элементов, а другой, который получает идентификатор и отображает детали для этой группы.
Если я сделаю что-то вроде этого:
Url.Action("Group", "Groups");
На главной странице сайта (/) он возвращает URL-адрес:
"mysite.com/Groups/Group"
что хорошо
Теперь, если текущий адрес сайта есть /Groups/Group/ 1
И я называю тот же метод
Url.Action("Group", "Groups");
возвращаемый url:
"mysite.com/Groups/Group/1"
Он автоматически добавляет значение маршрута для текущей страницы при создании URL-адреса.
Даже если я создаю URL таким образом:
Url.Action("Group", "Groups", null);
Таким образом, явно указывая, что я не хочу никаких значений маршрута, сгенерированный URL-адрес тот же.
Чтобы получить адрес, который я хочу, я должен явно указать значение маршрута в пустую строку, например:
Url.Action("Group", "Groups", new {id=""});
Это приведет к созданию следующего URL:
"mysite.com/Groups/Group"
Мой вопрос: почему это происходит? Если я не задаю никаких значений маршрута, он не должен добавлять их к сгенерированному URL.
Ответы
Ответ 1
Url.Action будет повторно использовать текущие параметры запроса, если вы их явно не задали. Он разработан по алгоритму исходящего url-соответствия. При поиске параметров данных маршрута в процессе генерации url параметры берутся из:
1) явно заданные значения
2) значения из текущего запроса
3) по умолчанию
В указанном выше порядке.
Исходящий алгоритм сопоставления для маршрутов является сложным, поэтому рекомендуется правильно задать все параметры для запроса, как это было в вашем примере
Ответ 2
Мое приложение явно задает значения маршрута и не хочет получать магическое значение из текущего запроса. Я хочу быть в полном контроле.
Я сделал расширение, которое сосуществует с моей коллекцией библиотеки маршрутов. Следовательно, один параметр RouteValueDictionary. (См. Комментарий моей библиотеки маршрутов внизу)
Здесь я удаляю любые значения маршрута из запроса до генерации URL-адреса.
(обратите внимание: для элемента array.contains ignorecase см.: Как я могу сделать Array.Contains нечувствительным к регистру в массиве строк?)
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);
}
}
У меня есть методы расширения формы и ActionLink, которые используют метод RemoveRoutes. Ни один помощник в моей библиотеке mvc не использует метод, который я создал. Таким образом, все маршрутизаторы очищаются до генерации URL-адресов.
Для справки я использую AttributeRouting. Вот пример одного маршрута из моей библиотеки маршрутов.
public static RouteValueDictionary DisplayNews(int newsId)
{
RouteValueDictionary route = new RouteValueDictionary();
route["Area"] = _area;
route["Controller"] = _controller;
route["Action"] = "DisplayNews";
route["newsId"] = newsId;
return route;
}
Ответ 3
Итак, когда я прочитал ответ объектного блока, я подумал, что мне придется изменить несколько ссылок в моем коде. Затем я попытался добавить маршрут по умолчанию, опустив параметры по умолчанию, которые решили проблему:
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+" }
);
Ответ 4
Простой пример:
public class ProductController : Controller
{
public ActionResult Edit(int id)
{
return View();
}
[Route("Product/Detail/{id:int}")]
public ActionResult Detail(int id)
{
return View();
}
}
Редактировать вид содержит только следующее:
@{ Layout = null;}
@Url.Action("Detail", "Cmr")
Итак, когда вы запускаете свой сайт, например. localhost:randomPort/Product/Edit/123
вы получите следующий ответ: /Product/Detail/123
Почему? Поскольку атрибут Route
требует параметра id
. Параметр Id считывается из url, хотя мы написали только Url.Action(methodName, controller)
- без указания параметра. Также нет смысла иметь деталь метода без идентификатора.
Чтобы атрибуты работали, следующая строка должна быть добавлена к RouteConfig.cs
:
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapMvcAttributeRoutes();
...
}
Ответ 5
Простым обходным решением было бы сначала вызвать
Url.Action("dummy", new { ... })
а затем переименуйте фикцию в результирующей строке в правильное имя действия.
Ответ 6
Глупый обходной путь, который я нашел, работает для обработки текущих данных маршрутизации страницы, включая изменение параметра страницы по вашим предпочтениям.
@{
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);
Ответ 7
Здесь обходной путь, который не требует маршрутизации атрибутов, и не изменяет текущие значения маршрута в вашем запросе. Эта идея исходит из http://notherdev.blogspot.ca/2013/10/aspnet-mvc-current-values-used-by-url-action.html, который я слегка изменил, чтобы работать без MvcFutures.
Я построил собственное расширение Action
на UrlHelper с дополнительным bool, которое позволяет необязательно игнорировать текущие значения маршрута (чтобы не столкнуться с существующими методами Action на UrlHelper.)
Что он делает, так это строит совершенно новый RequestContext из текущего, используя текущий маршрут, но не текущие значения маршрута. Затем мы передаем это вместе с базовыми помощниками. Таким образом, когда базовые помощники идут посмотреть, какие значения маршрута находятся в контексте запроса, они не найдут их и, следовательно, не будут использовать их при создании 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);
}