ASP.NET MVC Tips: 301 Redirect non-www versions of URL to www.

Search Engine Optimization guides, recommends to have one version of a URL of the same content. Search engines may pickup www and non-www versions of URL as 2 separate URLs, i.e. http://xyz.com/page1 may be considered different to http://www.xyz.com/page1. It is a good idea to pick on of these URLs as preferred and use 301 redirects to send traffic from the other URLS to the preferred URL. Today we will pickup www version of URL as our preferred URL and look at how we can redirect the non-www version of the URL to our preferred URL structure using ASP.NET MVC.


Workflow 

We can achieve the above by writing a custom handler that extends from the MvcHandler. We are basically interested in the ProcessRequest method where we can get hold of the non-www version of the URL to replace it to the www. version of URL.


    public class The301GlobalHandler : MvcHandler
{
public The301GlobalHandler(RequestContext requestContext) : base(requestContext) { }

protected override void ProcessRequest(HttpContextBase httpContext)
{
//We do not want this handler to process local requests
if (httpContext.Request.IsLocal)
base.ProcessRequest(httpContext);
else
ProcessExternalRequest(httpContext);
}

private void ProcessExternalRequest(HttpContextBase httpContext)
{
bool urlChanged = false;
string url = RequestContext.HttpContext.Request.Url.AbsoluteUri;
//Check for non-www version URL
if (!RequestContext.HttpContext.Request.Url.AbsoluteUri.Contains("www"))
{
urlChanged = true;
//change to www. version URL
url = url.Replace("http://", "http://www.");
}
ProcessExternalRequest(url, urlChanged, httpContext);
}

private void ProcessExternalRequest(string url, bool urlChanged, HttpContextBase httpContext)
{
if (urlChanged)
{
//mark as 301
httpContext.Response.Status = "301 Moved Permanently";
httpContext.Response.StatusCode = 301;
httpContext.Response.AppendHeader("Location", url);
}
else
base.ProcessRequest(httpContext);
}
}

The above code is self explanatory, we are checking if the Request is local or external, we are only interested on external URLs. Next we check, whether the RequestContext.HttpCOntext.Request.Url.AbsoluteUri contains a “www” or not. If it do not contain a “www” we go and inject a “www” to the URL to convert the non-www version of URL to a www. version URL. Then we mark the previous response to a 301, which indicates that the previous URL has been permanently removed and all future requests should be directed to the given new URL.

We have developed our core handler above, however we need a wrapper IRouteHandler class to get the above The301GlobalHandler to work within the MVC Framework. This wrapper class is responsible to return an instance of the The301GlobalHandler.


    public class Route301GlobalHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new The301GlobalHandler(requestContext);
}
}


We have got our Route301GlobalHandler ready, now we need all of our requests to go via this handler instead of the default. One easy way to do this, is to iterate through the route collection and  assign them to this Route301GlobalHandler during the Application_Start().


    protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
Route301Global.ReAssignHandler(RouteTable.Routes);
}

    public static class Route301Global
{
public static void ReAssignHandler(RouteCollection routes)
{
using (routes.GetReadLock())
{
AssignRoute301GlobalHandler(routes);
}
}

private static void AssignRoute301GlobalHandler(IEnumerable<RouteBase> routes)
{
foreach (var routeBase in routes)
{
AssignRoute301GlobalHandler(routeBase);
}
}

private static void AssignRoute301GlobalHandler(RouteBase routeBase)
{
var route = routeBase as Route;
if (route == null) return;
if (route.RouteHandler.GetType() != typeof(Route301Handler))
route.RouteHandler = new Route301GlobalHandler();

}
}

Note, if you have other custom RouteHandler in your application, you will need to tweak this code, I am assuming all routes that will be mapped in the RegisterRoutes(RouteCollection routes) method uses the MvcHandler.

Conclusion

We have identified keeping one set of URL is important for SEO. Then we have looked at how we can 301 redirect non-www version of URL to www. version URL in ASP.NET MVC. Hope this saves you some time and thank you for being with me so far.


Shahed Khan
CEO Simplexhub
Co-Founder PageFlakes
Founder Smart Code Generator

ASP.NET MVC Tips: Form POST, TryUpdateModel, unit test, Moq

Recently I have started playing with the Moq (pronounced “Mock-you” or just “Mock”) a Mocking Library for .NET Developers, that takes full advantage of .NET 3.5 (i.e. Linq expression trees) and C# 3.0 features. Here in this post I will discuss how I have used the TryUpdateModel method in the Form POST scenario and also share how I have written a test case using Moq mocking library to deal with the TryUpdateModel<TModel>(TModel model) method of the ASP.NET MVC controller.


I have a very simple user interface, that allows user to  enter data and submit, to add a new “User”.
CreateUser_WebForm


My View Page is:

CreateUser_View



and my “Action” is

CreateUser_Action  
You may have already noticed that I have decorated my action without any parameters, and my POST related codes are executed by checking the request type.

if (Request.RequestType == POST)
{
  //code
}

As I have no parameters in my CreateUser(), I did not need to create separate action  with AcceptVerbs(“GET”) and AcceptVert(“POST”) attribute. I have created an empty instance of MembershipCreateViewModel object and passed it to the TryUpdateModel() method to get the values of the posted form into my viewmodel object. MVC framework takes care of the rest and populates the viewModel with the desired values. Also note the RedirectToAction() method has been on success, it is  very important to perform this client – redirect. It will ensure that  the form does not resubmit, and the user will never see a prompt like this, if he hits the browser refresh button.

IE_resubmit 

My MembershipCreateViewModel class is as follows,


CreateUser_ViewModel 

I did not prefer passing four parameters to my CreateUser() Action such as,

[AcceptVerbs["POST"])
CreateUser(string username, string email, string password, string confirmPassword)
{
//code
}

instead I kept the method simple by using the MembershipCreateUserViewModel to communicated between the Controller and the View.

This is all good, but by now you may be wondering how would I test this Controller>Action. Lets explore that part. Here I will demonstrate my TestCase using the Moq framework, but you can do the same using other popular mocking frameworks like Rhino Mock, NMock, NMock2, NUnit.Mocks, EasyMock, TypeMock etc, or even with plain vanilla C# code. First of all I will need a FakeHttpContext to interact with my Controller from the test environment.

fakeContext


It is very straight forward to create a mock of a class or interface in Moq, I have created mocks of HttpContextBase, HttpRequestBase, HttpResponseBase, HttpSessionStateBase, HttpServerUtility above and decorated methods and properties that I use in my test cases using the Setup and SetupGet method that is available in the Moq library. 

Note how easily with couple of lines of code I have a mockup of the HttpContextBase. The Mock<T> allows creation of custom value matchers that can be used on setups and verification, completely replacing the built-in It class with our own argument matching rules. 


var httpContext = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>
httpContext.Setup(ctx => ctx.Request(request.Object);

This above code snippet is creating mock instances of HttpContexBase and HttpRequestBase classes, and then also setting up that httpContext.Request will return the mock instance “request”.

Now lets look at how I have used this mock httpContext in a test case.

EhrAdmin.MvcApplication - Microsoft Visual Studio (10) 
I have created a test case here that tests our CreateUser() action. Note I have created an instance of the ControllerContext and passed the httpContext mock object as a parameter of its constructor. After that I have assigned this instance ( context ) to the controller.ControllerContext property.

The Mock.Get() method is interesting it can retrieve a mock object for the given object instance. Here I have retrieved the request object and have modified it further. I have setup so that the request.Form returns NameValueCollection. You may be wondering why did I pass NamValueCollection. You may recall that I have used the TryUpdateModel() method in my action to get the form values into our viewmodel, and if you look what is happening under the hood of TryUpdateModel() method you will find that, “Form” is of type System.Collections.Specialized.NameValueCollection and a member of System.Web.HttpRequestBase, and
valuedictionary


the internals of TryUpdateModel() iterates through each item of the ContollerContext.HttpContext.Request.Form object and populates the matching properties of the viewmodel using reflection. The above code snippet from the ValueProviderDictionary class where we see that the keys are being read from the Form object to populate a dictionary.

Conclusion

Here in this post I have discussed a Form POST scenario and I have decorated my action method without any parameters as by default TryUpdateModel method looks at the submitted Form object and attempt to assign it to the viewmodel object that we pass. I have also demonstrated how I have used the Moq library to create fake/mock objects for my testcases, how I have setup some fake methods and properties. I have also demonstrated the handy Mock.Get method that can retrieve a mock object from a given object instance and allows us to modify the mock object further. Thank you for being with me so far and I hope this helps.


Shahed Khan
CEO Simplexhub
Co-Founder PageFlakes
Founder Smart Code Generator