Recently I came across requirement where in my event registration MVC web application,
user can simultaneously open app in different browser tab and register for the
event for multiple users. I was finding elegant way to handle this as by default
MVC maintains a single session across browser tabs and hence it is not possible
to differentiate the requests from same browser but originating from different
tabs. In a process of finding feasible solution to already developed app of
mine, I managed to learn 3 ways of achieving the same and so I thought to summarize
them and write an article on it.
Custom Route implementation
In this approach, we will implement Route definition that will put unique value in
RouteData for session. We will use inbuilt Guid.NewGuid() method to generate
this unique value. Let’s have a look at the whole code as per Listing 1.0 below.
namespace WebApplication1.Infrastructure
{
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
public class UniqueRoute : Route
{
private readonly bool isUnique;
public UniqueRoute(string uri, object defaults)
: base(uri, new RouteValueDictionary(defaults), new MvcRouteHandler())
{
isUnique = uri.Contains("guid");
DataTokens = new RouteValueDictionary();
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeData = base.GetRouteData(httpContext);
if (routeData == null)
return null;
if (!routeData.Values.ContainsKey("guid") ||
routeData.Values["guid"].ToString() == "")
routeData.Values["guid"] = Guid.NewGuid().ToString();
return routeData;
}
public override VirtualPathData GetVirtualPath
(RequestContext requestContext, RouteValueDictionary values)
{
return !isUnique ? null : base.GetVirtualPath(requestContext, values);
}
}
}
Listing 1.0 Custom Route definition using GUID
Now next step is to tell MVC framework about this and the right place is Global.asax.cs
file. Just add to RouteTable, the route definition we just created in Application_Start
event as following.
protected void Application_Start()
{
RouteTable.Routes.Add("Default", new UniqueRoute(
"{controller}/{action}/{id}",
new
{
controller = "Home",
action = "Index",
guid = "",
id = UrlParameter.Optional
}));
RouteTable.Routes.Add("UniqueRoute", new UniqueRoute(
"g/{guid}/{controller}/{action}/{id}",
new
{
controller = "Home",
action = "Index",
guid = "",
id = UrlParameter.Optional
}));
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Listing 1.1 Registering UniqueRoute with MVC RouteTable
The usage is pretty simple as shown in Code Listing 1.2 for any action method in
your controller.
private string GetUniqueKey()
{
return this.RouteData.Values["guid"].ToString();
}
private void SetUniqueSession
(string name, object value)
{
this.Session[this.GetUniqueKey() + "_" + name] = value;
}
public object GetSession(string name)
{
return this.Session[this.GetUniqueKey() + "_" + name];
}
Listing 1.2 Usage of UniqueRoute in controller
Using the methods as per listing 1.2, you can identify session and take appropriate
actions in code to maintain unique session across tabs.
Action method with Unique parameter value
This approach is very simple and we just need to have our action method to look for
unique value to identify whether it is new session or already existing session.
public ActionResult CreateRegistration(string guid)
{
if (guid == null)
{
return RedirectToAction("RegistrationView", new { guid = Guid.NewGuid().ToString() });
}
return this.View("RegistrationView");
}
Listing 1.3 Usage of GUID in action methods
As per listing 1.3, we check that if guid value is not present in the request, it
is new request and we create new value for this and redirect to action method
RegistrationView with created guid value. If it is present we simply return the
view.
Postback using JavaScript
In this approach, we can use client side JavaScript which is defined in to layout
page of your mvc application. Putting it in layout page will ensure that it will
be present in each and every page of your application. The script can be simple
as following:
if (typeof window.name != undefined) {
if (window.name == '') {
var date = new Date();
window.name = 'tab_' + date.getUTCHours() + date.getUTCMinutes() + date.getUTCSeconds() + d.getUTCMilliseconds();
}
var keyField = document.getElementById('UniqueKey');
keyField.value = window.name;
}
Listing 1.4 JavaScript for setting Unique key as window name
In the listing 1.4, we are generating unique combination by using hour, minutes,
seconds and milliseconds. We also need to store this value into the hidden field
so in every postback, it gets sent to server and so we can identify there whether
it is new session or existing one. For that just add below line of code in layout’s
cshtml page:
@Html.HiddenFor(model => model.'UniqueKey')
This way, you can have various ways to enable unique session across browser tabs
and you can choose best approach that suits your application.