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:

"/wp-content/images/D0A792E9EB199800BECC6564B70A38CA.png">
"border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;"
height="410" alt=
"ProtectedNamespaceController Factory that replaces DefaultControllerFactory"
src=
"/wp-content/images/A4EEFF877476F840B43EEC22FBD26F93.png"
width="724" border="0">

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:

"/wp-content/images/08A1DB4E22548512E7B8B498F1FD83E7.png">
"border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;"
height="241" alt=
"Using ProtectedNamespaceControllerFactory from Global.asax" src=
"/wp-content/images/4C7EDE0C29869262252C5949C00CC67A.png"
width="556" border="0">

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}

"/wp-content/images/529D1686365FB107B8E8583C546FDDA4.png">
"border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;"
height="111" alt=
"Creating a new route for the Controllers under the /API folder"
src=
"/wp-content/images/8790F281B46D6EB0FDE0A1EB36DDF44E.png"
width="513" border="0">

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.

"/wp-content/images/DEED4A8676AA350BA6AB24FF88546B16.png">
"border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;"
height="239" alt="image" src=
"/wp-content/images/6C63FAB799609169CDAAD82907E618CF.png"
width="761" border="0">

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:

"/wp-content/images/C8BAC0BA3A7CABE281E16289C427F904.png">
"border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;"
height="110" alt="image" src=
"/wp-content/images/874F1531AC6AE27D13B3641D970F7570.png"
width="540" border="0">

That’s it. Enjoy the full code from:

"http://code.msdn.microsoft.com/MvcWebAPI">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:

"http://msmvps.com/blogs/omar/archive/2008/10/03/create-rest-api-using-asp-net-mvc-that-speaks-both-json-and-plain-xml.aspx"
href=
"http://msmvps.com/blogs/omar/archive/2008/10/03/create-rest-api-using-asp-net-mvc-that-speaks-both-json-and-plain-xml.aspx">
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.

"http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmsmvps.com%2fblogs%2fomar%2fdefault.aspx">kick it on DotNetKicks.com src=
"http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmsmvps.com%2fblogs%2fomar%2fdefault.aspx"
border="0">

4 Comments

  1. Hello!

    Does this also lookup the views in the right folders?

    example: a call to /API/User/GetUserList renders the view from the folder ~/Views/API/User/GetUserList.aspx.

    how about the shared views? is it possible to also specify a module-dependent shared-folder. (like ~/Views/API/Shared/Error.aspx”)?

    best regards,

    Christian

  2. Thank you for submitting this cool story – Trackback from PimpThisBlog.com

  3. Please start being RESTful in your Views. SomeFolder/List, SomeFolder/New, etc.

    I hate this Get shit. This is like old web forms when you name shit like this.

    http://msdn.microsoft.com/en-us/magazine/dd943053.aspx
    http://wekeroad.com/2007/12/06/aspnet-mvc-using-restful-architecture

    If you are going to do MVC, do it right. What you have is bad practice and shouldn’t be tolerated.

    Read a book, about every good MVC book will talk to you and show you how your views should be RESTful. Rob Conery is the initial article everyone has used since the inception of ASP.NET MVC.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>