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
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 Action. Login 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 |
Now in IE, lets browse the URL Authentication/Login and see what happens.
User Successfully Logged in. UserName and SessionID shown - IE |
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 |
Now lets visit the Authentication/Login URL in FireFox as well to see what happens.
Now user is logged in even in FireFox |
Notice that the Http SessionId are different in two images |
That's it! we have successfully achieved Strongly Typed Session in an MVC3 application using Spring.NET.