Windows Phone 7 Developer Launch - Learn More
kick it on DotNetKicks.com   Shout it  

ASP.NET MVC Custom Routing Logic

I've been working with the new ASP.NET MVC framework since the first CTP back in December. I love it and it keeps getting better.

The application I'm working on uses custom Session management and so I have to manage the SessionId in the url myself. So I wanted to be able to make sure that all Redirects, Links and Urls had the SessionId. But I didn't want to have to add it myself (maintenance nightmare) and end up with having to track down errors from missing it.

Because the routing framework builds the urls for you I wanted to interject my logic into one place where I could intercept each request to build a url and add the SessionId.

Before the March CTP (aka the MIX '08 CTP) I had to add extensions to all the classes which constructed urls. Here's a link to a post of mine at the time: http://forums.asp.net/p/1216840/2159920.aspx#2159920

But now with the recent CTP refresh it has become very easy to manage the session id from one place. I just create a class which inherits from System.Web.Routing.Route (or you can implement it from scratch by inheriting from System.Web.Routing.RouteBase). Here's how I did it.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.Web;
using System.Web.Mvc;

using DevelopmentalMadness.Web.Mvc;

namespace DevelopmentalMadness.Web.MvcExtensions
{
public class SessionRoute : Route
{
public SessionRoute(String url, IRouteHandler routeHandler) : base(url, routeHandler)
{

}

public SessionRoute(String url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler)
{

}

public SessionRoute(String url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler)
{

}

public SessionRoute(String url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler)
{

}

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//how can I get the view context or controller context ?????
if (requestContext is ControllerContext)
{
ControllerContext cCtx = (ControllerContext)requestContext;

if (cCtx.Controller is SessionBaseController)
{
SessionBaseController sCtl = (SessionBaseController)cCtx.Controller;
if (values.ContainsKey("sid") == false && sCtl.ServerCookie != null)
values.Add("sid", sCtl.ServerCookie.Ticket);
}
}

VirtualPathData virtualPath = base.GetVirtualPath(requestContext, values);

return virtualPath;
}
}
}


SessionBaseController and ServerCookie are two custom classes of mine. ServerCookie is my session state management class and SessionBaseController is the class which exposes and maintains it. So I am able to access it through the RequestContext.

I haven't fully tested this yet, so I'm not sure if RequestContext will ever be something other than ControllerContext. I'm assuming that it can be, especially in the context of a unit test. Because they didn't explicitly declare ControllerContext it would be correct to assume that it won't always be ControllerContext, I just haven't discovered yet what the other possible values could be. Maybe for my next post.


The only other requirement is to design your routes so that they have a parameter for the values you are adding to the url, like this:




namespace MvcApplication
{
public class Global : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
// Note: Change Url= to Url="[controller].mvc/[action]/[id]" to enable
// automatic support on IIS6

routes.Add(new Route(
"Login.mvc/Index/{result}",
new RouteValueDictionary(new
{
controller = "Login",
action = "Index",
result = (String)null
}),
new MvcRouteHandler()
)
);

routes.Add(new SessionRoute(
"Login.mvc/{action}/{sid}",
new RouteValueDictionary(new
{
controller = "Login",
action = "Index",
sid = (String)null
}),
new MvcRouteHandler()
)
);

routes.Add(new SessionRoute(
"{controller}.mvc/{action}/{sid}",
new RouteValueDictionary(new
{
action = "Index",
sid = (String)null
}),
new MvcRouteHandler()
)
);

routes.Add(new Route(
"Default.aspx",
new RouteValueDictionary(new
{
controller = "Home",
action = "Index"
}),
new MvcRouteHandler()
)
);
}

protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
}
}


Each route which requires my custom session management uses SessionRoute instead of Route. Also, the route will include the "sid" argument in the route definition.



Now I can manage the routes I need to by simply updating my Routes defined in global.asax instead of worring wither or not every url in my application was written correctly.

kick it on DotNetKicks.com   Shout it  
Comments have been closed on this topic.
 

 

Copyright © Mark J. Miller