Unexpected route chosen while generating an outgoing url

天涯浪子 提交于 2019-12-04 23:40:47

This is a quick workaround I use to get it work:

public static MvcHtmlString GetPeriodLink(this HtmlHelper html, 
                                          RequestContext context, 
                                          DateTime date)
{
    UrlHelper urlHelper = new UrlHelper(context);

    context.RouteData.Values["month"] = date.Month;
    context.RouteData.Values["year"] = date.Year;

    return MvcHtmlString.Create(
              urlHelper.Action(
                 (string)context.RouteData.Values["action"]));
}

I simply remove the month and year entries from the context.RouteData.Values. I simply replace the month and year entries on the request context. If delete them from context (as I did at first), they would be unavailable for the helpers' methods invoked after this one.

This approach makes my extension method work by the scenario, described in Test 1 (please, see the question).


FINALY

Carefully rereading Sanderson S., Freeman A. - Pro ASP.NET MVC 3 Framework (3rd edition) I at least found the explanation of all this stuff:

Part 2 ASP.NET MVC in detail

Chapter 11 URLs, Routing and Areas

Generating outgoing URLs

in section Understanding segment variable reuse:

The routing system will reuse values only for segment variables that occur earlier in the URL pattern than any parameters that are supplied to the Html.ActionLink method.

As far as my month-year segment is met right after controller and I do specify values for month and year, all trailing segments (action,user) are not reused. As far as I do nor specify them in my anonymous object, they appear to be unavailable for the route. So, route1 cannot match.

In the book there is even a warning:

The best way to deal with this behavior is to prevent it from happening. We strongly recommend that you do not rely on this behavior, and that you supply values for all of the segment variables in a URL pattern. Relying on this behavior will not only make your code harder to read, but you end up making assumptions about the order in which your users make requests, which is something that will ultimately bite you as your application enters maintenance.

Well, it has bitten me)))

Worth to lose 100 rep to remember (I shall even repeat it here again) the rule: The routing system will reuse values only for segment variables that occur earlier in the URL pattern than any parameters that are supplied.

Would one route with a default for user be enough? That is

routes.MapRoute(
    "route1",
    "{controller}/{month}-{year}/{action}/{user}",
    new { user = "" }
);

Otherwise you would have to allow something more specific to the user route so it would not match route2 first when creating the url.

Update: I'd suggest leaving your helper and work directly with Url.Action, here are two tests for your scenario above:

 [TestFixture]
    public class RouteRegistrarBespokeTests
    {
        private UrlHelper _urlHelper;

        [SetUp]
        public void SetUp()
        {
            var routes = new RouteCollection();
            routes.Clear();
            var routeData = new RouteData();
            RegisterRoutesTo(routes);
            var requestContext = new RequestContext(HttpMocks.HttpContext(), 
                                           routeData);
            _urlHelper = new UrlHelper(requestContext, routes);
        }

        [Test]
        public void Should_be_able_to_map_sample_without_user()
        {
            var now = DateTime.Now;
            var result = _urlHelper.Action("Index", "Sample", 
                               new {  year = now.Year, month = now.Month });
            Assert.AreEqual(string.Format("/Sample/{0}-{1}/Index", 
                                      now.Month, now.Year ), result);
        }

        [Test]
        public void Should_be_able_to_map_sample_with_user()
        {
            var now = DateTime.Now;
            var result = _urlHelper.Action("Index", "Sample", 
                          new { user = "user1", year = now.Year, 
                                month = now.Month });
            Assert.AreEqual(string.Format("/Sample/{0}-{1}/Index/{2}", 
                                     now.Month, now.Year, "user1"), result);
        }



private static void RegisterRoutesTo(RouteCollection routes)
{
    routes.MapRoute("route1", "{controller}/{month}-{year}/{action}/{user}");
    routes.MapRoute("route2", "{controller}/{month}-{year}/{action}");
}

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!