Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC IgnoreRoute() by domain

UPDATE: rewording for conciseness...

With an ASP.NET MVC project, is it possible to have web.config rewrite rules take precedence over MVC's RegisterRoutes() call OR can IgnoreRoute be called for just a specific domain?

I have a single MVC application that accepts traffic over multiple domains (mydomain.com and otherdomain.com), the application serves different content based on the requested host (i.e. it's multi-tenant).

I have a URL rewrite (a reverse proxy) configured in web.config which should only apply to a specific host:

<rule name="Proxy" stopProcessing="true">
        <match url="proxy/(.*)" />
        <action type="Rewrite" url="http://proxydomain.com/{R:1}" />
        <conditions logicalGrouping="MatchAll">
            <add input="{HTTP_HOST}" pattern="^(mydomain\.com|www\.mydomain\.com)$" />
        </conditions>
        <serverVariables>
            <set name="HTTP_X_UNPROXIED_URL" value="http://proxydomain.com/{R:1}" />
            <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
            <set name="HTTP_X_ORIGINAL_HOST" value="{HTTP_HOST}" />
            <set name="HTTP_ACCEPT_ENCODING" value="" />
        </serverVariables>
    </rule>

However, an MVC application will seemingly only honor web.config configured routes if they're ignored from the application's RegisterRoutes() method with:

routes.IgnoreRoute("proxy");

Which then, unfortunately, applies the ignore to both domains. Suggestions greatly appreciated...

like image 475
DannyT Avatar asked Feb 14 '18 18:02

DannyT


1 Answers

can IgnoreRoute be called for just a specific domain?

Yes. However, since domains are completely ignored by .NET routing by default, you will need to customize routing to make IgnoreRoute specific to domains.

While it is possible to subclass RouteBase to do this, the simplest solution is to make a custom route constraint and use it to control the domains that a specific route will match. Route constraints can be used with the existing MapRoute, MapPageRoute and IgnoreRoute extension methods, so this is the least invasive fix to an existing configuration.

DomainConstraint

    public class DomainConstraint : IRouteConstraint
    {
        private readonly string[] domains;

        public DomainConstraint(params string[] domains)
        {
            this.domains = domains ?? throw new ArgumentNullException(nameof(domains));
        }

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, 
            RouteValueDictionary values, RouteDirection routeDirection)
        {
            string domain =
#if DEBUG
                // A domain specified as a query parameter takes precedence 
                // over the hostname (in debug compile only).
                // This allows for testing without configuring IIS with a 
                // static IP or editing the local hosts file.
                httpContext.Request.QueryString["domain"]; 
#else
                null;
#endif
            if (string.IsNullOrEmpty(domain))
                domain = httpContext.Request.Headers["HOST"];

            return domains.Contains(domain);
        }
    }

Note that for testing purposes, the above class accepts a query string parameter when the application is compiled in debug mode. This allows you to use a URL like

http://localhost:63432/Category/Cars?domain=mydomain.com

to test the constraint locally without having to configure the local web server and hosts file. This debugging feature is left out of the release build to prevent possible bugs (vulnerabilities) in the production application.

Usage

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // This ignores Category/Cars for www.mydomain.com and mydomain.com
        routes.IgnoreRoute("Category/Cars", 
            new { _ = new DomainConstraint("www.mydomain.com", "mydomain.com") });

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

NOTE: There is an overload that accepts a constraints parameter on all of the built-in route extensions, including IgnoreRoute and Area routes.

Reference: https://stackoverflow.com/a/48602769

like image 73
NightOwl888 Avatar answered Oct 28 '22 12:10

NightOwl888