Create ASP.NET MVC Controllers under Namespace and specific URL

When you have a lot of controllers, you need to organize them under Namespaces. Also, it is better to put controllers under subfolders in order to organize them properly and have some meaningful URL for them like /Admin/User where Admin is the subfolder and User is the controller. For example, you might have a lot of controllers that are APIs exposed by your web app, not regular pages. So, you might want to put them under /API/ folder. You also want to make sure no one can access those controllers from the root url. For example, no one must call /User/GetUserList instead they must call /API/User/GetUserList

ASP.NET MVC default routing and Controller Factory is very greedy, it ignores the subfolders inside the "Controllers" folder. There's a DefaultControllerFactory class in ASP.NET MVC which traverses all controllers under the "Controller" folder and creates a cache using just the class name as the key. So, it ignores any namespace or any subfolder where you have put the controller. So, I created a derivative of the default controller factory and created a new factory that checks if the requested controller belongs to any specific namespace and whether that controller must be inside a specific subfolder. You can map /Admin folder to respond to all controllers that are under then YourWebApp.Admin namespace. Similarly, you can map /API folder to respond to all controllers that are under the YourWebApp.API namespace. None of these controllers can be accessed outside the specified URL. Here's the factory that does it:

ProtectedNamespaceController Factory that replaces DefaultControllerFactory

This controller checks the namespace of the controller being returned by ASP.NET MVC's default implementation and ensures the requested URL is the right url where controllers from the namespace can be accessed. So, this means if the controller matches MvcWebAPI.API.UserController, it ensures the URL being requested must be /MvcWebAPI/API/User. It will return null if the URL was something else like /MvcWebAPI/User or /MvcWebAPI/SomeotherFolder/User.

Here's how you use it form Global.asax:

Using ProtectedNamespaceControllerFactory from Global.asax

You create a mapping for a Namespace and the subfolder it must belong to. Then you register the new Controller Factory as the default controller factory.

Now the second catch is, the default Route for the {controller}/{action}/{id} won't work for you. You need to create a specific router that starts with the subfolder name. For example, API/{controller}/{action}

Creating a new route for the Controllers under the /API folder

Here' two things to notice:

  • The use of PathStartWith routing constraint, which I will explain soon
  • The last parameter which tells the route to include the API namespace for this route. Otherwise it can't find the controllers in the API namespace

So, the PathStartsWith routing constraint ensures this route gets hit only when the requested URL is under the /API/ folder. For any other URL, it returns false and thus the routing handler skips this route.

image

It just does a comparison on the AbsolutePath of the current request URL to ensure the URL starts with the specified match.

Similarly, we need to tell the Default route to ignore all paths with /API. Here's how to do it:

image

That's it. Enjoy the full code from:

http://code.msdn.microsoft.com/MvcWebAPI

Read my previous post on creating Web API using ASP.NET MVC that can consume and expose Json and Xml:

Create REST API using ASP.NET MVC that speaks both Json and plain Xml

Note: incase you already read it, I have published new code that you should download.

kick it on DotNetKicks.com

1 Comment

Comments have been disabled for this content.