Using ProxyKit to Simulate IIS Rewrite Rules

In many ASP.NET apps I worked on, I used reverse proxy in a number of situations.  Typically I used IIS modules: Application Request Routing and URL Rewrite.  You can install both inside IIS using Microsoft Web Platform Installer.  In ASP.NET Core we cannot assume that the application is hosted on Windows inside IIS.  As I looked around for the replacement, I found this open source project: ProxyKit.  I created  small demo to verify that it works.  I picked a simple use case.  When I see a specific fragment inside the request URL, I would to create new URL by using predefined server and its path plus whatever follows the segment in question in the original URL. For example, I if I see request http://localhost:5001/my/site and my fragment is “my” and new URL is http://localhost:5002/site.

So, first step is to define our rules.  I am just going to dream up the the configuration:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "proxy": {
    "path": "/weather/v1/my/",
    "redirectTo": "http://localhost:5002/"
  }
}

My config is under proxy section.  Now I am going to use recipes to create my code.  Mainly I am basing this on conditional recipe.

using System;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ProxyKit;

namespace Site1
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddProxy();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            var testPath = Configuration["proxy:path"];
            var redirectTo = Configuration["proxy:redirectTo"];
            app.UseWhen(context =>
            {
                return Regex.IsMatch(context.Request.Path.ToString(), testPath, RegexOptions.IgnoreCase);
            },
            app =>
            {
                app.RunProxy(context =>
                {
                    var finalUrl = Regex.Replace(context.Request.Path.ToString().ToLower(), testPath, redirectTo, RegexOptions.IgnoreCase);
                    var finalContext = context.ForwardTo(finalUrl);
                    finalContext.UpstreamRequest.RequestUri = new Uri(finalUrl);

                    return finalContext
                        .CopyXForwardedHeaders()
                        .AddXForwardedHeaders()
                        .Send();
                });
            });

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

If you look at the UseWhen line, I am testing the request for the initial path in my Rewrite rule.  I am using regular expressions for performance reasons.  Then, when condition is met, I am creating new URL based on my requirements.  I am replacing parts of the URL with the replacement.  I am using ProxyKit’s ForwardTo method, but I also have to replace the URL in the forwarded context’s Upstream Request.  I am also adding standard forward headers.  So far I love this library, and wanted to share my excitement.  Thanks, Damian for your work.

Enjoy.

Leave a Reply

Your email address will not be published. Required fields are marked *