ASP.NET Core Rewrite Middleware Extensions

I’ve recently updated my site and one of the demo projects on it uses the Bing Image Search API to fetch a list of images and display them. This worked fine when running locally but when published it appeared that a number of the linked images weren’t displaying due to the HTTPS rewrite middleware upgrading the links to the external images and Chrome then blocking the images from sites which don’t have SSL enabled.

The code for enabling HTTPS rewrites is now part of the default Visual Studio templates and is obviously best practice but I hadn’t realised that it would upgrade links to external sites as well as the internal links which is necessary to prevent mixed content warnings in the browser.

In the end I didn’t manage to to prevent external image links from being upgraded but I did learn about how to create new rewrite middleware extensions.

This extension works the same as AddRedirectToHttps but also accepts a “hostName” parameter which is used to decide whether to apply the rule or not.

RewriteOptionsExtensions.cs

	
public static class RewriteOptionsExtensions
{
	public static RewriteOptions AddRedirectToHttpsHost(this RewriteOptions options, HostString host)
	{
		options.Rules.Add(new RedirectToHttpsHost(host));
		return options;
	}

	public class RedirectToHttpsHost : IRule
	{
		public int? SSLPort { get; set; }
		public int StatusCode { get; set; } = 302;
		public HostString Host { get; set; }

		public RedirectToHttpsHost(HostString host)
		{
			Host = host;
		}

		public RedirectToHttpsHost(HostString host, int statusCode)
		{
			Host = host;
			StatusCode = statusCode;
		}

		public RedirectToHttpsHost(HostString host, int statusCode, int? sslPort)
		{
			Host = host;
			StatusCode = statusCode;
			SSLPort = sslPort;
		}

		public virtual void ApplyRule(RewriteContext context)
		{
			if (!context.HttpContext.Request.IsHttps)
			{
				var host = context.HttpContext.Request.Host;

				if (Host == host)
				{
					if (SSLPort.HasValue && SSLPort.Value > 0)
					{
						// a specific SSL port is specified
						host = new HostString(host.Host, SSLPort.Value);
					}
					else
					{
						// clear the port
						host = new HostString(host.Host);
					}

					var req = context.HttpContext.Request;
					var newUrl = new StringBuilder().Append("https://").Append(host).Append(req.PathBase).Append(req.Path).Append(req.QueryString);
					var response = context.HttpContext.Response;
					response.StatusCode = StatusCode;
					response.Headers[HeaderNames.Location] = newUrl.ToString();
					context.Result = RuleResult.EndResponse;
				}
			}
		}
	}
}
	

Startup.cs

	
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
	if (env.IsDevelopment())
	{
		app.UseBrowserLink();
		app.UseDeveloperExceptionPage();
	}
	else
	{
		// Redirect all HTTP requests to HTTPS
		RewriteOptions options = new RewriteOptions();
		options.AddRedirectToHttpsHost(new HostString("www.bitscry.com"));

		app.UseRewriter(options);

		app.UseExceptionHandler("/Home/Error");
	}

	app.UseStaticFiles();

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

The solution I settled on instead of this was to create an Azure Function to act as a proxy for my images so that they would be upgraded to HTTPS.


Leave a Reply

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