Question

The following code:

 <li>@Html.ActionLink(metaTapp.Nav_About, "Mayla", "About")</li>
                                <li>@Html.ActionLink(metaTapp.Nav_Support, "Support", "About")</li>
                                <li>@Html.ActionLink(metaTapp.Nav_Exchange, "Index", "Exchange")</li>
                                <li>@Html.ActionLink("Post Rfq", "Create", "Rfq")</li>

is producing the following links:

   <li><a href="/Upload/Image?action=Mayla&amp;controller=About">About</a></li>
                                <li><a href="/Upload/Image?action=Support&amp;controller=About">Support</a></li>
                                <li><a href="/Upload/Image?action=Index&amp;controller=Exchange">Exchange</a></li>
                                <li><a href="/Upload/Image?action=Create&amp;controller=Rfq">Post Rfq</a></li>

My Global Application Start looks like this:

  protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            UploadRouteConfig.RegisterRoutes(RouteTable.Routes);
            LocalizationConfig.RegisterRoutes(RouteTable.Routes);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory());
        }
    }

UploadRouteConfig.RegisterRoutes:

 public static void RegisterRoutes(RouteCollection routes)
    {

        routes.MapRoute("Upload", "Upload/Image", null).RouteHandler = new UploadMvcRouteHandler();
    }

LocalizationConfig.RegisterRoutes

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapRoute(
            "Account", // Route name
            "Account/{action}", // URL with parameters
            new { controller = "Account", action = "Index" } // Parameter defaults
    );

    routes.MapRoute(
      "RfqCategory",
      string.Format("{{{0}}}/Rfq/CategoryFilter/{{category}}", Constants.ROUTE_PARAMNAME_LANG),
      new { controller = "Rfq", action = "CategoryFilter", category = Guid.Empty.ToString() }
    );

    routes.MapRoute(
        Constants.ROUTE_NAME,
        string.Format("{{{0}}}/{{controller}}/{{action}}/{{rfqid}}", Constants.ROUTE_PARAMNAME_LANG),
        new { controller = "About", action = "Home", rfqid = "00000000-0000-0000-0000-000000000000" }
    );
}

RouteConfig.RegisterRoutes:

 public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute("TappDefault", "{controller}/{action}/{id}", new { controller = "About", action = "Home", id = UrlParameter.Optional }
        );

I cant understand how upload is being matched to everything. If the route doesn't start with Upload/Image it should fall through to the localizationconfig routes?

Was it helpful?

Solution

Ok so the short answer to my problem is that I am rendering ActionLinks when I should be using RouteLinks. ActionLink will perform a match based on route table entries which seems like what I want but because I'm using a static Url:

"Upload/Image"

every url is matched. Why? Because routes are not filters. Routes work by matching supplied route values to the parameters of segments of the Url, but since:

"Upload/Image"

has no parameters i.e. {controller} then technically EVERYTHING is a match. RouteLink on tyhe pother hand allows me to specify which route to use when rendering the link:

@Html.RouteLink(
linkText: "route: Home",
routeName: "TappDefault",
routeValues: new {controller="About", action="Home"}
)

..

From Professional ASP.NET.MVC4 (Jon Galloway, Phil Haack, Brad Wilson, K. Scott Allen)

Page 232 Chapter 9 Routing:

Let’s suppose you add the following page route at the beginning of your list of routes so that the URL /static/url is handled by the page /aspx/SomePage.aspx:

routes.MapPageRoute("new", "static/url", "~/aspx/SomePage.aspx");

Note that you can’t put this route at the end of the list of routes within the RegisterRoutes method because it would never match incoming requests. Why wouldn’t it? Well, a request for /static/url would be matched by the default route and never make it through the list of routes to get to the new route. Therefore, you need to add this route to the beginning of the list of routes before the default route.

NOTE This problem isn’t specific to Routing with Web Forms. There are many cases where you might route to a non-ASP.NET MVC route handler.

Moving this route to the beginning of the defined list of routes seems like an innocent enough change, right? For incoming requests, this route will match only requests that exactly match /static/url but will not match any other requests. This is exactly what you want. But what about generated URLs? If you go back and look at the result of the two calls to Url.RouteLink, you’ll find that both URLs are broken:

/url?controller=section&action=Index&id=123

and

/static/url?controller=Home&action=Index&id=123

This goes into a subtle behavior of Routing, which is admittedly somewhat of an edge case, but is something that people run into from time to time.

Typically, when you generate a URL using Routing, the route values you supply are used to “fill in” the URL parameters as discussed earlier in this chapter.

When you have a route with the URL {controller}/{action}/{id}, you’re expected to supply values for controller, action, and id when generating a URL. In this case, because the new route doesn’t have any URL parameters, it matches every URL generation attempt because technically, “a route value is supplied for each URL parameter.” It just so happens that there aren’t any URL parameters. That’s why all the existing URLs are broken, because every attempt to generate a URL now matches this new route.

This might seem like a big problem, but the fix is very simple. Use names for all your routes and always use the route name when generating URLs. Most of the time, letting Routing sort out which route you want to use to generate a URL is really leaving it to chance, which is not something that sits well with the obsessive-compulsive control freak developer. When generating a URL, you gener- ally know exactly which route you want to link to, so you might as well specify it by name. If you have a need to use non-named routes and are leaving the URL generation entirely up to Routing, we recommend writing unit tests that verify the expected behavior of the routes and URL generation within your application.

Specifying the name of the route not only avoids ambiguities, but it may even eke out a bit of a per- formance improvement because the Routing engine can go directly to the named route and attempt to use it for URL generation.

In the previous example, where you generated two links, the following change fixes the issue. We changed the code to use named parameters to make it clear what the change was. @Html.RouteLink( linkText: "route: Test", routeName: "test", routeValues: new {controller="section", action="Index", id=123})

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top