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

Как предотвратить Url.RouteUrl(...) от наследования значений маршрута от текущего запроса

Допустим, у вас есть метод действий для отображения продуктов в корзине покупок.

 // ProductsController.cs
 public ActionMethod Index(string gender) {

      // get all products for the gender
 }

В другом месте, в мачте, который отображается на каждой странице, вы используете Url.RouteUrl для создания ссылок HREF на другие страницы сайта:

 <a href="<%= Url.RouteUrl("testimonials-route", new { }) %>" All Testimonials </a>

Этот testimonials-route определяется в global.ascx первым маршрутом ниже. Обратите внимание, что приведенный выше вызов RouteUrl не предоставляет gender, но маршрут определяется по умолчанию как "нейтральный", поэтому мы ожидаем, что будет вызван отзыв .Index( "нейтральный" ).

 routes.MapRoute(
  "testimonials-route",
  "testimonials/{gender}",
  new { controller = "Testimonials", action = "Index", gender = "neutral" },
  new { gender = "(men|women|neutral)" }
 );

routes.MapRoute(
  "products-route",
  "products/{gender}",
  new { controller = "Products", action = "Index", gender = (string)null },
  new { gender = "(men|women|neutral)" }
 );

Если кто-то посещает страницу /products/women, мы получаем HREF до /testimonials/women Если кто-то посещает страницу /products, тогда мы получаем пустой HREF (вызов RouteUrl возвращает null).

Но это не имеет смысла? testimonials-route по умолчанию используется для 'neutral' для нас, если мы не укажем для него значение маршрута?

Что получается, так это то, что вспомогательное расширение Url.RouteUrl(routeName, routeValues) сначала будет искать в своем параметре routeValues для значения маршрута gender, и если он не найдет его в этом словаре, он будет смотреть на текущий URL-адрес, который мы находимся (помните, что Url - это объект UrlHelper, который имеет контекст текущего доступного ему запроса).

Это может иметь приятный эффект от предоставления нам ссылки на отзывы мужчин, если мы находимся на странице продукта для мужчин, но это, вероятно, не то, что мы хотим, если мы не передали значение в вызове RouteUrl, и явно указывается "нейтраль" в качестве значения по умолчанию в файле global.asax.cs.

В случае, когда мы посетили /products/, мы вызвали маршрут 'products-route', и был вызван метод Products(null). Вызов Url.RouteUrl() на самом деле наследует это значение null для gender, когда мы создаем URL-адрес, используя testimonials-route. Несмотря на то, что мы указали значение по умолчанию для gender в 'testimionials-route ', оно все еще использует это нулевое значение, которое приводит к сбою маршрута и RouteUrl возвращает значение null. [note: маршрут не работает, потому что у нас есть ограничение на (men | women | neutral), а null не подходит, что]

На самом деле становится более страшно - в том, что "контроллер" и "действие" могут быть унаследованы одинаково. Это может привести к тому, что URL-адреса генерируются полностью неверным контроллером даже при вызове RouteUrl (...) с явным именем маршрута, имеющим контроллер по умолчанию.

В этом случае, как только вы это выяснили, вы можете легко устранить его разными способами, но в других случаях это может привести к некоторому опасному поведению. Это может быть по дизайну, но это определенно страшно.

4b9b3361

Ответ 1

Мое решение было следующим:

Вспомогательный метод HtmlExtension:

    public static string RouteUrl(this UrlHelper urlHelper, string routeName, object routeValues, bool inheritRouteParams)
    {
        if (inheritRouteParams)
        {
            // call standard method
            return urlHelper.RouteUrl(routeName, routeValues);
        }
        else
        {
            // replace urlhelper with a new one that has no inherited route data
            urlHelper = new UrlHelper(new RequestContext(urlHelper.RequestContext.HttpContext, new RouteData()));
            return urlHelper.RouteUrl(routeName, routeValues);
        }
    }

Теперь я могу сделать:

Url.RouteUrl('testimonials-route', new { }, false)

и точно знать, что он всегда будет вести себя одинаково независимо от контекста.

Как он работает, нужно взять существующий UrlHelper и создать новый с пустым 'RouteData'. Это означает, что наследование (даже нулевое значение) не наследуется.

Ответ 2

Может быть, мне что-то не хватает, но почему бы не настроить его так:

В своем глобальном asax создайте два маршрута:

routes.MapRoute(
    "testimonial-mf",
    "Testimonials/{gender}",
    new { controller = "Testimonials", action = "Index" }
);

routes.MapRoute(
    "testimonial-neutral",
    "Testimonials/Neutral",
    new { controller = "Testimonials", action = "Index", gender="Neutral" }
);

Затем используйте Url.Action вместо Url.RouteUrl:

<%= Url.Action("Testimonials", "Index") %>
<%= Url.Action("Testimonials", "Index", new { gender = "Male" }) %>
<%= Url.Action("Testimonials", "Index", new { gender = "Female" }) %>

Который, на моей машине, решает следующие URL:

/Testimonials/Neutral 
/Testimonials/Male 
/Testimonials/Female

Я не уверен, что это приемлемое решение, но это альтернатива, нет?