Page redirection and URL Rewriting with ASP.NET Core

When you develop a web application, you often need to add some redirection rules. The most common redirection rules are: redirect from "http" to "https", add "www", or move a website to another domain. URL rewriting is often use to provide user friendly URL.

Before we start, I want to explain the difference between redirection and rewrite. Redirecting sends a HTTP 301 or 302 to the client, telling the client that it should access the page using another URL. The browser will update the URL visible in the address bar, and make a new request using the new URL. On the other hand, rewriting happens on the server, and is a translation of one URL to another. The server will use the new URL to process the request. The client doesn't know that the server has rewritten the URL.

With IIS, you can use the web.config file to define the redirection and rewrite rules or use RewritePath:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Redirect to https">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="Off" />
            <add input="{REQUEST_METHOD}" pattern="^get$|^head$" />
            <add input="{HTTP_HOST}" negate="true" pattern="localhost" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

However, this doesn't work anymore with ASP.NET Core. Instead, you can use the new NuGet package: Microsoft.AspNetCore.Rewrite (GitHub). This package is very easy to use. Open the startup.cs file and edit the Configure method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ISiteProvider siteProvider)
{
    app.UseRewriter(new RewriteOptions()
        .AddRedirectToHttps()
        .AddRedirect(@"^section1/(.*)", "new/$1", (int)HttpStatusCode.Redirect)
        .AddRedirect(@"^section2/(\\d+)/(.*)", "new/$1/$2", (int)HttpStatusCode.MovedPermanently)
        .AddRewrite("^feed$", "/?format=rss", skipRemainingRules: false));

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

The rules are based on regex and substitutions. The regex is evaluated on the HttpContext.Request.Path, which does not include the domain, nor the protocol. This means you cannot redirect to another domain or add "www" using this method, but don't worry, I will show you how to do this after!

Microsoft has decided to help you using this new package. Indeed, if you already have a web.config file or even an .htaccess file (.net core is cross platform) you can import them directly:

app.UseRewriter(new RewriteOptions()
    .AddIISUrlRewrite(env.ContentRootFileProvider, "web.config")
    .AddApacheModRewrite(env.ContentRootFileProvider, ".htaccess"));

If you have complex rules that can't be expressed using a regex, you can write your own rule. A rule is a class that implements Microsoft.AspNetCore.Rewrite.IRule:

// app.UseRewriter(new RewriteOptions().Add(new RedirectWwwRule()));

public class RedirectWwwRule : Microsoft.AspNetCore.Rewrite.IRule
{
    public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;
    public bool ExcludeLocalhost { get; set; } = true;

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;
        var host = request.Host;
        if (host.Host.StartsWith("www", StringComparison.OrdinalIgnoreCase))
        {
            context.Result = RuleResult.ContinueRules;
            return;
        }

        if (ExcludeLocalhost && string.Equals(host.Host, "localhost", StringComparison.OrdinalIgnoreCase))
        {
            context.Result = RuleResult.ContinueRules;
            return;
        }

        string newPath = request.Scheme + "://www." + host.Value + request.PathBase + request.Path + request.QueryString;

        var response = context.HttpContext.Response;
        response.StatusCode = StatusCode;
        response.Headers[HeaderNames.Location] = newPath;
        context.Result = RuleResult.EndResponse; // Do not continue processing the request
    }
}

Last but not least, you can debug your redirections and rewrites using the log. Every standard rules write a line in the log when they transform the URL:

And voilà. This is very simple to add redirection and rewrite rules in ASP.NET Core 😃

Leave a reply