Search code examples
asp.netasp.net-mvcasp.net-mvc-4asp.net-mvc-routing

Relative links and URL rewrite in ASP.NET MVC


I have an ASP.NET MVC application that's used by different companies. The URL for the app looks something like this: http://myapp.com/companyA/stuff or http://myapp.com/companyB/stuff. I get the company identifier from the URL in Application_BeginRequest and then do a RewritePath to http://myapp.com/stuff.

My question is about relative links that ASP.NET MVC generates. It appears that these links are generated to the root of the site, disregarding company identifiers in the URL. For example, once at http://myapp.com/companyA/stuff, @Url.Action("index", "home") creates a link that points to http://myapp.com/ not http://myapp.com/companyA/

Is there a way to instruct ASP.NET MVC to use a certain baseURL when generating relative links?

Thanks.


Solution

  • The problem is that you are using URL rewriting. URL rewriting is a one-way map - URL to resource.

    The modern (and more MVC-centric) way of doing things is to use .NET routing. Routing is a 2-way map from URL to resource and from resource back to URL. A resource is identified by a (preferably unique) set of route values.

    Routing in MVC is very flexible. The MapRoute extension method can be used to create quite a range of options. However, you can literally create any URL scheme you want by inheriting either the Route class or RouteBase class. You can literally use anything in the request (URL, headers, cookies, session state, etc) to route to a resource (usually a controller action). See this answer for an advanced example.

    But in your case, you can simply set up a route to map to the correct company.

    routes.MapRoute(
        name: "Company",
        url: "{company}/{action}",
        defaults: new { controller = "Company", action = "Index" },
        constraints: new { company = @"companyA|companyB" }
    );
    
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
    

    Then you just need a company route (a controller for each company is another option, it depends on how much you want to share).

    public class CompanyController : Controller
    {
        // CompanyA/
        // CompanyA/Index
        // CompanyB/
        // CompanyB/Index
        public ActionResult Index()
        {
            return View();
        }
    
        // CompanyA/Stuff
        // CompanyB/Stuff
        public ActionResult Stuff()
        {
            return View();
        }
    }
    

    Then in your application, you need to specify the company when building the ActionLinks, which will use the routing framework to build the URL.

    @Url.ActionLink("Home", "Index", "Home", new { company = "companyA" }, null)
    @Url.ActionLink("Home", "Index", "Home", new { company = "companyB" }, null)
    

    An alternative option that might be better for this scenario is to use Areas to separate each company. Also see the MSDN tutorial. Areas each have their own route and they act like different containers within an MVC application.

    But either way, you should really get rid of the URL rewriting because that is an antiquated way of working with URLs.