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...
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.
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.
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, includingIgnoreRoute
and Area routes.
Reference: https://stackoverflow.com/a/48602769
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With