Sunday, October 2, 2011

Strongly Typed Session Using MVC3 and Spring.NET - Part - 3

This is the last post in the series of posts to show How we can achieve Strongly Typed Session using MVC3 and Spring.NET.  In previous two posts (first and second) we saw what were the benefits of strongly typed session and how we could enable the session scope using Spring.NET 1.3.1.

The question that we had left unanswered in the previous post was:  

What is "session" scope supposed to do with Strongly typed sessions?

Well the session scope is the key, in this post we will see how it enables us to create a strongly typed session.

The session scope enables us, to register objects with scope=session.  Spring.NET will create one instance per user Session for objects that are configured with scope=session.  This means effectively we can use such an object to hold information that would otherwise be saved in the HTTP Session.  Since, these objects are configured in Spring.NET, they can be injected as dependencies in any other class. 

Where would we want to inject such a class?

MVC Controllers are a good place to do so.

Why? 

Because, controllers are the class that would do some business processing and then would want to set some information in the HTTP Session.

For e.g. Lets consider this scenario, we have a AuthenticationController which has a Login action.  This method takes in the username and password as argument.  This method calls the AuthenticationService to find whether the username and password combination is valid.  If the username and password are valid, AuthenticationController wants to set the username in the session so that it could be used by other pages to show user information on the page.

In our case we do not want to use the HTTP Session.  Hence, our AuthenticationController will put the username in some object, let's call it UserContext, which has been configured in Spring.NET with scope="session".

But wait,

What if two users are trying to login at the same time?  Will one thread overwrite the username saved in the UserContext by the other thread?

As the UserContext object is configured with scope="session", Spring.NET is smart enough to understand that, two requests are coming  to AuthenticationController are coming from two different user sessions, it injects two different instances of UserContext in the AutheticationController for two different user sessions.  Hence, one thread will *not* overwrite the username set in the UserContext by the other thread. 

So far so good.

What happens when, user who had logged in earlier (i.e. UserContext has the UserName property set), sends another request to the server?  How do we ensure that correct UserContext instance is injected in the Controller so that previously set UserName could be retrieved?  

Again, Spring.NET is smart enough to understand that, for the given user session an instance of UserContext was already created, Spring.NET will find the correct UserContext instance associated with the user session and injects it in other Controllers/objects that need it.  This effectively eleminates the direct use of HTTP Session for us.  We can be sure that, whatever information we set in UserContext will be specific for a given User Session and correct UserContext instance will be injected in the controller whenever we need it.

I know, at first it all looks very confusing, but lets look at the code to make things clear.

These are the steps required to configure the UserContext and AuthenticationController
  • Register the UserContext in Spring.NET configuration file with scope="session"
  • Write the implementation for the UserContext
  • Register the AuthenticationController in Spring.NET configuration flie with singleton="false".  Add to this the dependency of UserContext.
  • Write implementation of Login Action to set the UserName Property in the UserContext.  Write another action method let's call it UserInfo which will retrieve the UserName Property from the UserContext object and display it on the UI
  • Write the RazorIndex view to show the UserName from the ViewBag 
  • Test that the setup is working with two different browser windows trying to Login and access the UserInfo at the same time.  We should see correct user names being displayed based on where the request is coming from
Lets look at individual steps in detail

Register the UserContext in Spring.NET configuration file with scope="session"

Update the web.xml and add the configuration for UserContext as follows
Notice that we have registered the UserContext object with scope="session". Spring.NET will understand that the UserContext object is registered with singleton but in session scope i.e. One UserContext object per user session.

Write the implementation for the UserContext 

The UserContext class would be simple enough.
Register the AuthenticationController in Spring.NET configuration flie with singleton="false"Add to this the dependency of UserContext

Update the web.xml to add the configuration for AuthenticationController as follows. 
Write implementation of Login Action to set the UserName Property in the UserContext

AuthenticationController will have a Login Action and UserInfo ActionLogin Action will set the UserName in the UserContext.  For demonstration purpose we will append the SessionId to the UserName.  This will show us that, when we request the login action method from two different browser windows we will see different user name on the UI.
Write the RazorIndex view to show the UserName from the ViewBag

The RazorIndex.cshtml will simply show the UserName from the ViewBag if its not null. Else it will show no user logged in yet message.
Test that the setup is working with two different browser windows trying to login and access the UserInfo at the same time

Enough theory, lets see the setup in action.  As they say,

Real test is in the real world!

Let's try and browse the URL Authentication/UserInfo from FireFox and Internet Explorer and see what happens

User not logged in - FireFox

User not logged in -IE
As the UserName is not set in UserContext we see no user name set message.

Now in IE, lets browse the URL Authentication/Login and see what happens.
User Successfully Logged in.  UserName and SessionID shown - IE
As we can see, the page redirect to the Authentication/UserInfo User name with session id is shown.

Lets reload the Authentication/UserInfo the page on FireFox.  Remember that, we have not yet visited the Login page on FireFox, but we have visited the Login page on IE.
Even after user is logged in on IE, he/she is not logged in on FireFox
Yep even though we have set the UserName in the user session started from IE, it does not effect the UserContext reference in the User Session started from FireFox.

Now lets visit the Authentication/Login URL in FireFox as well to see what happens.


Now user is logged in even in FireFox





As you can see it redirects to Authentication/UserInfo with UserName set.  Notice that the session id in FireFox window is different from the session id in IE.
Notice that the Http SessionId are different in two images

This effectively proves that UserContext reference plugged in by Spring.NET when request comes from IE is different from the one that gets plugged in when request comes in from FireFox i.e. We have successfully injected different UserContext reference per User Session. UserContext object can now be used to hold any information that would have be placed in HTTP Session. This effectively eliminates the use of HTTP Session and gets us a strongly typed object which can be used as a replacement to the HTTP Session.

That's it! we have successfully achieved Strongly Typed Session in an MVC3 application using Spring.NET.
Have some Fun!