If anyone is interested, I have figured out how to do CakePHP style prefix routing with ASP.NET MVC 2.
Feel free to post changes or suggestions! I have spent hours combing google to try to fit these peices together.
(Please excuse the pasted code, the code inserter was messing up the line breaks.)
First of all if you are not familiar with cakephp, it is an MVC framework for PHP that is similar to Rails.
It has a concept of prefix routing (sometimes called admin routing). What this does is turn a route like this: /admin/controller/action into controller::admin_action.
It basically extracts the 'admin' part out of the Url and prefixes the action name with it. This is beneficial because it allows you to have separate actions in the same controller that are targeted for an administrative account, or any other use you can
think of. By using the actionlink generator, if you do not specify a 'prefix' it will assume that you are using the same prefix. This is great because now you can use partial views for navigation in both views 'admin_index' and 'index' and it will remember
which prefix you are using. (DRY) ex: <%=html.actionlink('name','someaction')%> if used in an admin view will generate a link to admin_someaction.
With .NET MVC 2 there is a new concept called 'Areas' that allows you to separate your application into sections for ease of development. (Similar to CakePHP plugins). But this does not quite achceive the same thing. It requires you to have separate controllers.
This is good for different purposes.
Anyway, My implementation will support both 'Areas' and prefix routing. However the url will look like this:
/area/controller/prefix/action or just simply: /controller/prefix/action for the main application. It will do the same thing as the cakephp routing but the route elements will be in a different order.
First we need to create a route handler class that will support this 'prefix' concept:
Public Function GetHttpHandler(ByVal requestContext As System.Web.Routing.RequestContext) As System.Web.IHttpHandler Implements System.Web.Routing.IRouteHandler.GetHttpHandler
If (requestContext.RouteData.Values.ContainsKey("prefix")) Then
' Prefix is present
Dim prefix As String = requestContext.RouteData.Values("prefix")
If Not String.IsNullOrEmpty(prefix) Then
' Prefix is not empty
Dim action = requestContext.RouteData.Values("action")
This handler basically just looks at the route values and extracts a 'prefix' out. It then renames the action to 'prefix_action' before handing control to the default Mvc Handler.
Now open up your global.asax and add a new route definition to handle the prefix routing. ( I added a constraint so that it will pick up the prefix if it is either 'admin' or 'manager'
As you can see, i added a route called 'Main_Prefix' It tells the system to use the 'PrefixRouteHandler' for the request which will handle the prefix.
* I also added a namespace into the datatokens. This is because I may have controllers with the same name in different areas. It tells the system to look in that namespace for the controller. And not to be confused about the other controllers with the
same name.
This will pick up the prefix routes and deal with them accordingly.
Part 2: Areas support.
Now when you add a new 'Area' to your application. To support this same feature in that area, Add a similar route definition to your AreaRegistration Class.
(My area was called 'Budget') So I want to be able to do this: /budget/controller/prefix/action
As you can see, I added the route: 'Budget_Prefix'
It has two constraints:
{area} must be 'Budget' -- this stops it from interfering with other routes.
{prefix} must be either 'admin', or 'manager'
* this is important!:
I added the 'area' token to the 'DataTokens' - This will ensure that the new mvc 2 areas support will be able to resolve your view name! It must be set to the name of your Area. I also added the namespace for the same reasons as before.
Also, I changed the default route for this area to include the {area} token. Im not sure if this is necessary but it works.
WIth these changes you will be able to do these routes:
/controller/action/id - resolves to : controller.action(id)
/area/controller/action/id - resolves to: (area) controller.action(id)
/controller/prefix/action/id - resolves to: controller.prefix_action(id)
/area/controller/prefix/action/id - resolves to: (area) controller.prefix_action(id)
I hope my ramblings will help someone who is trying to figure this out!
d3m3r5u5
0 Points
1 Post
ASP.NET MVC 2 prefix routing (similar to CakePHP) with Areas support
Jan 22, 2010 10:00 PM|LINK
If anyone is interested, I have figured out how to do CakePHP style prefix routing with ASP.NET MVC 2.
Feel free to post changes or suggestions! I have spent hours combing google to try to fit these peices together.
(Please excuse the pasted code, the code inserter was messing up the line breaks.)
First of all if you are not familiar with cakephp, it is an MVC framework for PHP that is similar to Rails.
It has a concept of prefix routing (sometimes called admin routing). What this does is turn a route like this: /admin/controller/action into controller::admin_action.
It basically extracts the 'admin' part out of the Url and prefixes the action name with it. This is beneficial because it allows you to have separate actions in the same controller that are targeted for an administrative account, or any other use you can think of. By using the actionlink generator, if you do not specify a 'prefix' it will assume that you are using the same prefix. This is great because now you can use partial views for navigation in both views 'admin_index' and 'index' and it will remember which prefix you are using. (DRY) ex: <%=html.actionlink('name','someaction')%> if used in an admin view will generate a link to admin_someaction.
With .NET MVC 2 there is a new concept called 'Areas' that allows you to separate your application into sections for ease of development. (Similar to CakePHP plugins). But this does not quite achceive the same thing. It requires you to have separate controllers. This is good for different purposes.
Anyway, My implementation will support both 'Areas' and prefix routing. However the url will look like this: /area/controller/prefix/action or just simply: /controller/prefix/action for the main application. It will do the same thing as the cakephp routing but the route elements will be in a different order.
First we need to create a route handler class that will support this 'prefix' concept:
(prefixrouting.vb)
-------------------------------------------------------------------------------------------------
Imports System.Web
Imports System.Web.Mvc
Imports System.Web.Routing
Namespace Util.Routing
Public Class PrefixRouteHandler
Implements IRouteHandler
Public Function GetHttpHandler(ByVal requestContext As System.Web.Routing.RequestContext) As System.Web.IHttpHandler Implements System.Web.Routing.IRouteHandler.GetHttpHandler
If (requestContext.RouteData.Values.ContainsKey("prefix")) Then
' Prefix is present
Dim prefix As String = requestContext.RouteData.Values("prefix")
If Not String.IsNullOrEmpty(prefix) Then
' Prefix is not empty
Dim action = requestContext.RouteData.Values("action")
If action Is Nothing Then
action = "index"
End If
' Rename the action to 'prefix_action'
requestContext.RouteData.Values("action") = prefix & "_" & action
End If
End If
Return New MvcHandler(requestContext)
End Function
End Class
End Namespace
----------------------------------------------------------------------------------------------------
This handler basically just looks at the route values and extracts a 'prefix' out. It then renames the action to 'prefix_action' before handing control to the default Mvc Handler.
Now open up your global.asax and add a new route definition to handle the prefix routing. ( I added a constraint so that it will pick up the prefix if it is either 'admin' or 'manager'
---------------------------------------------------------------------------------------------------
Imports Util.Routing
Public Class MvcApplication
Inherits System.Web.HttpApplication
Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
routes.IgnoreRoute("{*favicon}", New With {.favicon = "(.*/)?favicon.ico(/.*)?"})
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}")
routes.Add("Main_Prefix", _
New Route("{controller}/{prefix}/{action}/{id}", New PrefixRouteHandler()) _
With {.Constraints = New RouteValueDictionary(New With {.prefix = "(admin|manager)"}), _
.Defaults = New RouteValueDictionary(New With {.controller = "Home", .action = "Index", .id = ""}), _
.DataTokens = New RouteValueDictionary(New With {.namespaces = New String() {"MyApplication.Controllers"}})})
' MapRoute takes the following parameters, in order:
' (1) Route name
' (2) URL with parameters
' (3) Parameter defaults
routes.MapRoute( _
"Default", _
"{controller}/{action}/{id}", _
New With {.controller = "Home", .action = "Index", .id = ""}, _
Nothing, _
New String() {"MyApplication.Controllers"} _
)
End Sub
Sub Application_Start()
AreaRegistration.RegisterAllAreas()
RegisterRoutes(RouteTable.Routes)
'RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes)
End Sub
End Class
----------------------------------------------------------------------------------------------------
As you can see, i added a route called 'Main_Prefix' It tells the system to use the 'PrefixRouteHandler' for the request which will handle the prefix.
* I also added a namespace into the datatokens. This is because I may have controllers with the same name in different areas. It tells the system to look in that namespace for the controller. And not to be confused about the other controllers with the same name.
This will pick up the prefix routes and deal with them accordingly.
Part 2: Areas support.
Now when you add a new 'Area' to your application. To support this same feature in that area, Add a similar route definition to your AreaRegistration Class.
(My area was called 'Budget') So I want to be able to do this: /budget/controller/prefix/action
Here is my AreaRegistration Class:
----------------------------------------------------------------------------------------
Imports Util.Routing
Namespace Areas.Budget
Public Class BudgetAreaRegistration
Inherits AreaRegistration
Public Overrides ReadOnly Property AreaName() As String
Get
Return "Budget"
End Get
End Property
Public Overrides Sub RegisterArea(ByVal context As System.Web.Mvc.AreaRegistrationContext)
context.Routes.Add("Budget_Prefix", _
New Route("{area}/{controller}/{prefix}/{action}/{id}", New PrefixRouteHandler()) _
With {.Constraints = New RouteValueDictionary(New With {.area = "Budget", .prefix = "(admin|manager)"}), _
.Defaults = New RouteValueDictionary(New With {.controller = "Home", .action = "Index", .id = ""}), _
.DataTokens = New RouteValueDictionary(New With {.area = "Budget", .Namespaces = New String() {"MyApplication.Areas.Budget.Controllers"}})})
context.MapRoute( _
"Budget_default", _
"{area}/{controller}/{action}/{id}", _
New With {.controller = "Home", .action = "Index", .id = ""}, _
New With {.area = "Budget"}, _
New String() {"MyApplication.Areas.Budget.Controllers"} _
)
End Sub
End Class
End Namespace
---------------------------------------------------------------------------------------
As you can see, I added the route: 'Budget_Prefix'
It has two constraints:
* this is important!:
I added the 'area' token to the 'DataTokens' - This will ensure that the new mvc 2 areas support will be able to resolve your view name! It must be set to the name of your Area. I also added the namespace for the same reasons as before.
Also, I changed the default route for this area to include the {area} token. Im not sure if this is necessary but it works.
WIth these changes you will be able to do these routes:
I hope my ramblings will help someone who is trying to figure this out!
mvc 2 cakephp areas prefix routing
ricka6
All-Star
15070 Points
2272 Posts
Microsoft
Moderator
Re: ASP.NET MVC 2 prefix routing (similar to CakePHP) with Areas support
Jan 23, 2010 12:31 AM|LINK
Thanks for posting this. I added it to the MVC FAQ.