Showing posts with label ASP.NET MVC. Show all posts
Showing posts with label ASP.NET MVC. Show all posts

Thursday, 23 May 2013

Session Expiration in ASP.NET MVC

The other day a colleague of mine asked me for some help fixing a bug. The issue was that our web sessions are configured to last 30 minutes and after 30 minutes of inactivity ASP.NET should redirect the user back to the login page (in our case at least), but this was not happening in our latest ASP.NET MVC website. I wandered over to his screen and saw it for myself, it just let the request complete as usual, defying my understanding of how session expiration worked.

So I searched around and found out the following:

Authorization

In our case the session timeout and the forms authentication timeout are different: sessions timeout at 30 minutes while forms authentication can last much longer. So it seems that what ASP.NET MVC does by default (using the [Authorize] attribute) is to authorize the request coming in because that has not expired yet but a new session is set up for the user. This means that any previous data you had stored is missing which will no doubt affect the functionality of your website.

Solution

So how can we solve this? This solution is actually quite simple, a custom authorization attribute can be implemented which can both authorize and check the session. For example let’s assume that your session contains something which you know means that a user is logged in, for instance a user ID; that means you can write something like the following:

[AttributeUsage(
AttributeTargets.Class
| AttributeTargets.Method,
Inherited
= true,
AllowMultiple
= true
)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}

// Authenticate the user using Forms Authentication
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}

// Now check that a new session has not been created, if we are missing
// some critical session variables then we assume that the session
// expired and do not allow the request to continue
if (httpContext.Session["UserId"] == null)
{
return false;
}

return true;
}
}

By applying this custom authorization attribute to your controllers/actions you will now be able to check both forms authentication and session expiration at the same time.

Friday, 18 January 2013

ASP.NET Websites and Sub-Folders in Project Output Paths

This will be a quick post but I wanted to get it out there while I remembered.

Yesterday and this morning at work I was faced with an unusual problem. One of our ASP.NET MVC websites, which worked perfectly fine until now, decided it did not want to compile. Specifically it was the Razor engine which wasn’t happy; the actual project would compile fine in Visual Studio but as soon as I ran it a runtime compiler error would happen immediately because a collection of Razor helpers could not find our own assembly as a reference. What’s going on?

So I searched and searched and found all sorts of questions on StackOverflow explaining how to add an assembly reference to the Web.config file and add it to the specific Razor Web.config settings but that wasn’t helping me at all; it simply refused to run.

Out of sheer frustration I started comparing the project folder to a previous version to see what could have possibly changed to break it and the only difference I found was that another developer had checked in a change which would compile the project output from “bin\” to “bin\{Configuration}”. Surely that can’t be the cause, can it?

Actually it was, and this blog post by Adam Craven explained why:

Intellisense in Razor for Custom Types and Helpers

Now most Visual Studio project templates will setup the output path of the compiled files to be “bin\{Configuration}”, which sounds sensible to me. Apparently ASP.NET projects don’t do this because the ASP.NET runtime will not find your own assemblies for some reason – as I said, the blog post above explains it better than I could.

So all in all a frustrating morning was resolved simply by changing the project output path. It’s funny how the simplest thing can completely break your code.

Wednesday, 16 January 2013

Unit Testing ASP.NET MVC Views

I have successfully jumped onto the Test Driven Development (TDD) bandwagon now and loving it. Over the last 6 months I’ve been trying to incorporate it wherever I can; my code at work is now starting to gain a number of unit tests within a codebase that initially I thought would be difficult to do automated testing on.

Recently I’ve started a personal project using ASP.NET MVC – I haven’t had much experience of MVC, only Web Forms, and wanted to expand my knowledge on .NET web development. I’ve heard many stories how ASP.NET MVC was designed from the ground up to be very easy to test so I jumped at the chance of reading up and experimenting with it. One bonus as well is that this would be all-new code; the one thing I’ve learned about TDD is that it works brilliantly with a clean-slate, with legacy code it requires quite a bit more work and slower iterations in order to not break what you’ve already got.

For most of the MVC framework I could clearly see how you could apply unit testing to your code as it focuses a lot on plain objects. One of the things I couldn’t initially get my head around though is testing controllers and verifying that views returned were correct.

Take this example of a simple controller:

public class AccountController : Controller
{
public ActionResult Index(int id)
{
Account account
= new Account()
{
Id
= id,
Name
= "My Account"
};
return View("Index", account);
}
}

It is actually quite easy to write a unit test for this controller and action as, thankfully, controllers are not strongly tied to anything to do with HTTP requests or responses, so testing becomes as simple as:


[Fact]
public void IndexActionReturnsView()
{
AccountController controller
= new AccountController();
ActionResult result
= controller.Index(42);
Assert.NotNull(result);
}

But at this point I got a little stuck. How do I know that the controller action returned the view that I wanted? Initially I was thinking only in terms of controller actions returning HTML output; how can we verify HTML text output in automated tests?


The answer is you don’t, and actually the answer is a lot simpler and cleaner; we don’t verify the overall output, we simply verify that the view has the necessary information (i.e. model data) it needs to generate the HTML output (or whatever output format is required).


This can be split up into several parts:


Check the View Name


One test can be defined to determine that the correct view was returned by checking the name of the view, such as below:


[Fact]
public void IndexActionReturnsCorrectViewName()
{
AccountController controller
= new AccountController();

// Cast the result of the action to a ViewResult, this will then provide
// view information for the test to confirm
ViewResult result = controller.Index(42) as ViewResult;

Assert.Equal(
"Index", result.ViewName);
}

One caveat I’ve found with this though is that in the controller you must specifically request which view you want; letting the MVC framework determine it by convention – based on the action name – doesn’t seem to work for some reason.


Check the View Model Type


The next test you can run is to ensure that the view returned was supplied with the correct model type, like so:


[Fact]
public void IndexActionReturnsCorrectViewModelType()
{
AccountController controller
= new AccountController();
ViewResult result
= controller.Index(42) as ViewResult;

// Test that the model object passed to the view was the correct
// type
Assert.IsType<Account>(result.Model);
}

Check the View Model Data


Finally you can then test that the view returned was supplied with the correct model data, like so:


[Fact]
public void IndexActionReturnsCorrectViewModelData()
{
AccountController controller
= new AccountController();
ViewResult result
= controller.Index(42) as ViewResult;

// Cast the model in the view result to the correct type and
// now we can test against it
Account model = result.Model as Account;

Assert.Equal(
42, model.Id);
}

Conclusion


This concept, once understood, feels incredibly clean to me. If you think about it you don’t want to test how a web page looks because that could change over time thanks to re-designs, plus parsing such information would be incredibly painful. All you need to do is verify that the view was given enough details for it to carry out it’s job; can it display a title for the page, does it have the correct list of customer orders to render, etc. It has actually made me re-think some of my code designs to better match this concept.


I can see why developers love ASP.NET MVC compared to Web Forms now!