1
votes

In my MVC project, I've Item controller and some actions like Index.

The RouteConfig includes:

  routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );

In some views, I'm using the helper method Html.ActionLink("Items","Index","Item") to create anchors for the index action. So the href of the anchor result will be (/Item/Index)

Now, I need to map the following static URL:

/IndirectItem/Index

to the Index action of the Item controller with default parameter (indirect = true), so the RouteConfig will be:

 routes.MapRoute(
                name: "IndirectItem",
                url: "IndirectItem/Index",
                defaults: new { controller = "Item", action = "Index", indirect = true  }
            );
   routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

It seems OK and the client requests were mapped correctly , but all anchors resulted from the Html.ActionLink("Items","Index","Item") helper method were mapped to the URL (/IndirectItem/Index) instead of (/Item/Index).

How can I fix this problem without changing all Html.ActionLink() to Html.RouteLink() or adding another route for the original url ?

3

3 Answers

2
votes

Using constraints will be a convenient solution for your problem.

Use the following IndirectItem route instead of yours.

routes.MapRoute(
           name: "IndirectItem",
           url: "{staticVar}/{action}",
           defaults: new { controller = "Item", action = "Index", indirect = true},
           constraints: new { staticVar = "IndirectItem" }
        );

and you don't need any change in the Default route.

It works fine with me.

0
votes

You are experiencing this issue because Html.ActionLink uses Routing table for generating URLs and since IndirectItem route is match to Html.ActionLink("Items","Index","Item") (because it has Index action and Item controller specified in both route and action link). The resolving done by a first match so the order of routes registration matters

By adding DefaultItem route:

routes.MapRoute(
     name: "DefaultItem",
     url: "Item/Index/{id}",
     defaults: new { controller = "Item", action = "Index", id = UrlParameter.Optional  }
);

prior to your current routes:

routes.MapRoute(
       name: "IndirectItem",
       url: "IndirectItem/Index/,
       defaults: new { controller = "Item", action = "Index", indirect = true}
);
routes.MapRoute(
       name: "Default",
       url: "{controller}/{action}/{id}",
       defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Should fix the issue

Another option could be creating empty IndirectItem controller that inherits from Item controller:

public IndirectItemController : ItemController
{
}

and then changing the route to

routes.MapRoute(
       name: "IndirectItem",
       url: "IndirectItem/Index/,
       defaults: new { controller = "IndirectItem", action = "Index", indirect = true}
); 
0
votes

The answers given by Omar Gohar and Alex Art are misleading.

The problem you are running into is that your route does not match when generating the URL. This is simply because you have not provided all of the route values to create a match in your ActionLink.

@Html.ActionLink("Items", "Index", "Item", new { indirect = true }, null)

If changing your ActionLink declaration is not an option, you can attach your "indirect" metadata to the route using the DataTokens parameter.

You use the DataTokens property to retrieve or assign values associated with the route that are not used to determine whether a route matches a URL pattern. These values are passed to the route handler, where they can be used for processing the request.

routes.MapRoute(
    name: "IndirectItem",
    url: "IndirectItem/Index",
    defaults: new { controller = "Item", action = "Index" }
).DataTokens = new RouteValueDictionary(new { indirect = true });

The bottom line is that RouteValues (which are populated by the defaults if not provided by the URL pattern) are not meant to be used for metadata. They are meant to be real data to match to make the URL unique.

Of course, if you are not actually using the indirect route value for anything, you can simply omit it from the route.

routes.MapRoute(
    name: "IndirectItem",
    url: "IndirectItem/Index",
    defaults: new { controller = "Item", action = "Index" }
);