RestfulRouting is a routing library for ASP.NET MVC based on the Rails 3 routing DSL.
public class Routes : RouteSet { public Routes() { Resource<SessionsController>(); Resources<BlogsController>(() => { As("weblogs"); Only("index", "show"); Collection("latest", HttpVerbs.Get); Resources<PostsController>(() => { Except("create", "update", "destroy"); Resources<CommentsController>(() => Except("destroy")); }); }); } } // Global.asax.cs public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new RestfulRoutingViewEngine()); RouteTable.Routes.MapRoutes<Routes>(); } }
Clone the repository and run rake. This will compile and test the project. Now go to the build folder and grab the 2 assemblies you need to reference from your project.
git clone git://github.com/stevehodgkiss/restful-routing.git cd restful-routing rake
All routes are defined inside the constructor of a class which inherits from RouteSet. You can then connect the main RouteSet to your application routes using RouteTable.Routes.MapRoutes
There are 2 types of resource mappings that you can use. Resource<TController>() and Resources<TController>() (Notice the pluralization). Resources maps a collection of resources. Resource maps a singleton. The two main differences are that Resource doesn’t have an {id} route parameter, and it doesn’t have an index.
This maps a sessions resource as a singleton which has a nested avatars singleton resource.
Resource<SessionsController>(() =>
{
Resource<AvatarsController>();
});
The RouteTable will look like this with the above mapping:
| Http Method | Path | Endpoint (controller#action) |
| GET | session | sessions#show |
| POST | session | sessions#create |
| GET | session/new | sessions#new |
| GET | session/edit | sessions#edit |
| PUT | session | sessions#update |
| DELETE | session | sessions#destroy |
| GET | session/avatar | avatars#show |
| POST | session/avatar | avatars#create |
| GET | session/avatar/new | avatars#new |
| GET | session/avatar/edit | avatars#edit |
| PUT | session/avatar | avatars#update |
| DELETE | session/avatar | avatars#destroy |
This maps a blogs resource with a nested posts resource.
Resources<BlogsController>(() =>
{
Resources<PostsController>();
});
The RouteTable will have routes which look like this with the above mapping:
| Http Method | Path | Endpoint (controller#action) |
| GET | blogs | blogs#index |
| POST | blogs | blogs#create |
| GET | blogs/{id} | blogs#show |
| GET | blogs/new | blogs#new |
| GET | blogs/{id}/edit | blogs#edit |
| PUT | blogs/{id} | blogs#update |
| DELETE | blogs/{id} | blogs#destroy |
| GET | blogs/{blogId}/posts | posts#index |
| POST | blogs/{blogId}/posts | posts#create |
| GET | blogs/{blogId}/posts/{id} | posts#show |
| GET | blogs/{blogId}/posts/new | posts#new |
| GET | blogs/{blogId}/posts/{id}/edit | posts#edit |
| PUT | blogs/{blogId}/posts/{id} | posts#update |
| DELETE | blogs/{blogId}/posts/{id} | posts#destroy |
You can adjust the behaviour of a given resource mapping in the context of it's nested action parameter.
Resources<PostsController>(() => {
As("weblogs"); // changes the path name to weblogs
Only("index", "show") // only maps the index and show actions
Except("destroy") // maps all actions except destroy
Constrain("id", "\d+"); // constrains the id to digits
});
Use the Member and Collection methods inside the resource to add routes to the collection (without an id route parameter) or the member (with an id route parameter). Methods available to map are Get, Post, Put, Delete and Head.
Resources<PostsController>(() => {
Collection(x => {
x.Get("latest"); // GET posts/latest => posts#latest
x.Put("someaction") // PUT posts/someaction => posts#someaction
});
Member(x => x.Post("like")); // POST posts/1/like => posts#like(1)
});
Area<Controllers.Admin.BlogsController>("admin", () =>
{
Resources<BlogsController>(() =>
{
Resources<PostsController>(() =>
{
Resources<CommentsController>();
});
});
});
This maps an area called admin and will only look for controllers located in the Controllers.Admin namespace. You can specify that the whole RouteSet is to be put into an area without putting a path prefix on the mapping. The following would just constrain all the routes to the namespace of BlogsController, but still look for views based on the area name blogs.
Area<BlogsController>("blogs");
RestfulRoutingViewEngine inherits from WebFormsViewEngine and changes where it looks for area views slightly. It changes it from {area}/Views/{controller}/{action} to Views/{area}/{controller}/{action}.
The connect method allows you to pull in routes from another routeset.
public class BlogRoutes : RouteSet { public BlogRoutes() { Area<BlogsController>("blogs"); Resources<BlogsController>(); Resources<PostsController>(); } } public class Routes : RouteSet { public Routes() { // maps the BlogRoutes route set without a path prefix. Connect<BlogRoutes>(""); } } // Application_Start() RouteTable.Routes.MapRoutes<Routes>();
Two additional methods are provided so you can add custom routes to the route set if you need to. Map is a chainable method that allows you to add standard mappings. Route is a method that will allow you to add a custom route to the route set.
Map("posts/{year}/{slug}") .To<PostsController>(x => x.Post(-1, "")) .Constrain("slug", @"\w+") .Constrain("year", @"\d+"); Route(new Route("posts/{action}", new RouteValueDictionary(new { controller = "posts" }), new MvcRouteHandler());