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 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 |
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.