Подтвердить что ты не робот

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.

4b9b3361

Ответ 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);
}