Introduction
ISAPI module to intercepts IIS response.redirect calls and convert the URL to lowercase to avoid 302 or 301 Status code not good for SEOs.
Background
An application containing more than 100 files having code using Response.Redirect to achieve redirection from a page to another page in ASP.NET. If passed urls to Response.Redirect are case insensitive and SEOs complaining for them to be in lowercase.
So quick and dirty solution could be go in all code files and convert those URLs passed to Response.Redirect method to lowercase. But obviously this not a smart solution. More sophisticated and quick solution is to intercept all redirected Requests and convert their redirected URLs to lowercase.
After googling and going to MSDN and also on StackOverflow manage to write a IHttpModule while same could be done in global.asax file. But for separation of concern IHttpModule is better. For a understanding of IHttpModule please refer to this article. I am putting both logic below. Please always be careful in implementing any thing in IIS pipeline It is good to have CODE REVIEW with Team Lead and other senior developers, other wise there could be very unpredictable results and performance degrading issues could be raised.
Main logic
From MSDN documentation It is quite clear that Response.Redirect is internally setting Location Header in Response object and luckily, not only this header is accessible but also could be alter. It means that I have to get the value of Location header and then call lower method on it and then set it back to header.
string redirectedLocation = context.Response.Headers["Location"];
context.Response.Headers["Location"] = redirectedLocation.ToLowerInvariant();
And there is also a property of Response object indicating redirection for the current request
IsRequestBeingRedirected
Global.asax
void Application_EndRequest(object sender, EventArgs e)
{
HttpApplication application = sender as HttpApplication;
HttpContext context = application.Context;
if (context.Response.IsRequestBeingRedirected)
{
string redirectedLocation = context.Response.Headers["Location"];
context.Response.Headers["Location"] = redirectedLocation.ToLowerInvariant();
}
}
IHttpModule
As mentioned above IHttpModule is cleaner and more maintainable as well. Please put contants files to avoid magic strings in a separate file some where else in project better is to have them project specific.
namespace CustomizeIISPipeline
{
public class RedirectorInterceptorModule : IHttpModule
{
public RedirectorInterceptorModule()
{
}
public void Init(HttpApplication application)
{
application.EndRequest +=
(new EventHandler(this.RedirectInterceptor));
}
private void RedirectInterceptor(object sender, EventArgs e)
{
HttpApplication application = sender as HttpApplication;
HttpContext context = application.Context;
if (context.Response.IsRequestBeingRedirected)
{
string redirectedLocation = context.Response.Headers[AppConstants.RESPONSE_LOCATION_HEADER];
context.Response.Headers[AppConstants.RESPONSE_LOCATION_HEADER] = redirectedLocation.toLowerInvariant();
}
}
public void Dispose(){}
}
public static class AppConstants
{
public const string RESPONSE_LOCATION_HEADER = "Location";
}
}
web.config entry for above module
<system.webServer>
<modules>
<add name="RedirectorInterceptorModule" type="CustomizeIISPipeline.RedirectorInterceptorModule"/>
</modules>
</system.webServer>
Points of Interest
Though above is a remedy for the situation I and mine team is confronting with because of not well planned coding stratedgy. However, a better solution could be that we always have a Wrapper class whenever utliziing underlying framework specially for URL related functionality and any 3rd Party consumption in our applications.
So rule of thumb,
"Having wrapper will avoid regret in future for changes to make"