Chitika

June 27, 2012

ASP.NET MVC 4 WebAPI. Support Areas in HttpControllerSelector

This article was written for ASP.NET MVC 4 RC (Release Candidate). If you are still using Beta version of ASP.NET MVC 4 then you have to read the previous article.

HttpControllerFactory was deleted in ASP.NET MVC 4 RC. Actually, it was replaced by two interfaces: IHtttpControllerActivator and IHttpControllerSelector.

Unfortunately DefaultHttpControllerSelector still doesn't support Areas by default. To support it you have to write your HttpControllerSelector from scratch. To be honest, I will derive my selector from DefaultHttpControllerSelector.

In this post I will show you how you can do it.

AreaHttpControllerSelector

First of all, you have to derive your class from DefaultHttpControllerSelector class:

    public class AreaHttpControllerSelector : DefaultHttpControllerSelector
    {
        private readonly HttpConfiguration _configuration;

        public AreaHttpControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            _configuration = configuration;
        }
    }

In the constructor mentioned above I called the base constructor and stored the HttpConfiguration. We will use it a little bit later.

My code will use two constants:

        private const string ControllerSuffix = "Controller";
        private const string AreaRouteVariableName = "area";

You can understand why we need first one by name. The second one contains the name of the variable which we will use to specify area name in Routes collection.

Somewhere we have to store all of the API controllers.

        private Dictionary<string, Type> _apiControllerTypes;

        private Dictionary<string, Type> ApiControllerTypes
        {
            get { return _apiControllerTypes ?? (_apiControllerTypes = GetControllerTypes()); }
        }

        private static Dictionary<string, Type> GetControllerTypes()
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            var types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix) && typeof(IHttpController).IsAssignableFrom(t)))
                .ToDictionary(t => t.FullName, t => t);

            return types;
        }

Method GetControllerTypes takes all the API controllers types from all of your assemblies, and store it inside the dictionary, where the key is FullName of the type and value is the type itself.
Of course we will set this dictionary only once. And then just use it.

Now we are ready to implement one of the important method:

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            return GetApiController(request) ?? base.SelectController(request);
        }

In that method I try to take the HttpControllerDescriptor from method GetApiController and if it return null then call the base method.

And additional methods:

        private static string GetAreaName(HttpRequestMessage request)
        {
            var data = request.GetRouteData();

            if (!data.Values.ContainsKey(AreaRouteVariableName))
            {
                return null;
            }

            return data.Values[AreaRouteVariableName].ToString().ToLower();
        }

        private Type GetControllerTypeByArea(string areaName, string controllerName)
        {
            var areaNameToFind = string.Format(".{0}.", areaName.ToLower());
            var controllerNameToFind = string.Format(".{0}{1}", controllerName, ControllerSuffix);

            return ApiControllerTypes.Where(t => t.Key.ToLower().Contains(areaNameToFind) && t.Key.EndsWith(controllerNameToFind, StringComparison.OrdinalIgnoreCase))
                    .Select(t => t.Value).FirstOrDefault();
        }

        private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
        {
            var controllerName = base.GetControllerName(request);

            var areaName = GetAreaName(request);
            if (string.IsNullOrEmpty(areaName))
            {
                return null;
            }

            var type = GetControllerTypeByArea(areaName, controllerName);
            if (type == null)
            {
                return null;
            }

            return new HttpControllerDescriptor(_configuration, controllerName, type);
        }
Method GetAreaName just takes area name from HttpRequestMessage.

Method GetControllerTypeByArea are tries to find the controller in the ApiControllerTypes by full name of the controller where the full name contains area's name surrounded by "." (e.g. ".Admin.") and ends with controller name + controller suffix (e.g. UsersController).

And if a controller type found then method GetApiController will create and return back HttpControllerDescriptor.

So, my AreaHttpControllerSelector is ready to be registered in my application.

Registering AreaHttpControllerSelector

The next thing you have to do is to say to your application to use this controller selector instead of DefaultHttpControllerSelector. And fortunately it is really easy - just add one additional line to the end of Application_Start method in Glogal.asax file:
        protected void Application_Start()
        {
            // your default code
                    GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new AreaHttpControllerSelector(GlobalConfiguration.Configuration));
        }
That's all.

Using AreaHttpControllerSelector

If you did everything right, now you can forget about that "nightmare" code mentioned above. And just start to use it!

You have to add new HttpRoute to your AreaRegistration.cs file:

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.Routes.MapHttpRoute(
                name: "Admin_Api",
                routeTemplate: "api/admin/{controller}/{id}",
                defaults: new { area = "admin", id = RouteParameter.Optional }
            );

            // other mappings
        }

Or just use one global route in your Global.asax like:


            routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{area}/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

That's all. Good luck, and have a nice day.

81 comments:

  1. This is great but it doesn't work if you are passing a complex type in the Get as per below:

    public IEnumerable Get([FromUri] ApiCriteria apiCriteria)

    When you pass values over in the query string that map to the complex type we get:

    No action was found on the controller 'Apis' that matches the request.

    but without the query string values it finds it.

    It works fine using the DefaultHttpControllerSelector and I haven't yet got to the bottom as to why.

    Shame as I could really do with using it to seperate my controllers into areas.

    ReplyDelete
    Replies
    1. Thanks for the comment.
      Yes, for now it is not so easy to fix it.

      But there is one workaround. You can create a special route and pass the parameters in a URL. So, you can split your complex type to simple types.

      For example, instead of:
      public IEnumerable Get(Person person) { ... }
      You can use:
      public IEnumerable Get(string firstName, string lastName) { ... }

      and map the route:
      routes.MapHttpRoute(
      name: "DefaultApiPerson",
      routeTemplate: "api/{area}/{controller}/{firstName}/{lastName}"
      );

      I hope it would help.

      Delete
    2. Hello Andrew and Anonymous.

      I found a solution that supports splitting webapi's across areas while maintaining querystring functionality. The trick is to store the area name in the route's DataTokens, rather than the default parameter list. The latter will confuse the routing engine and break querystring support. The updated version of Andrew's AreaHttpControllerSelector can be found in a blog post I just finished writing:

      ASP.NET MVC 4 RC: Getting WebApi and Areas to play nicely

      Thank you, Andrew, for writing this post. It helped me get on the right track! :)

      Delete
    3. Thank you Martin, for finish this topic.

      Delete
  2. Interestingly the namespaces default value extension got added to the MapHttpRoute in RC. This provides me with, (instead of using areas and this selector), another way of creating granular controllers grouped by namespaces but this suffers the same issue with not honouring the [FromUri] attribute.

    It is a pain because [FromUri] is the way to decorate and consume complex types in the Get methods for sure.

    ReplyDelete
  3. I decided to use your code as a base for a solution for versioning APIs:

    http://www.tuomistolari.net/blog/2012/8/23/webapi-convention-based-versioning-with-accept-headers.html

    ReplyDelete
  4. This is great ! I spent hours figuring out how to make Web Api and Areas work together and with your code, it just worked !

    Thanks a lot.

    ReplyDelete
  5. Your article was helpful, but I'm a little confused on what this gets you?

    I was able to add an area "Whatever" and add using System.Web.Http statement to the WhateverAreaRegistration class and map the route via context.Routes.MapHttpRoute in the RegisterArea method and the route get() just worked.

    I then thought maybe you were trying to make it such that you don't need to add the route in each area registration class, but I was able to accomplish that by removing what I did above and then doing the following in the Global.asax.cs Application_Start - RegisterRoutes method:

    routes.MapHttpRoute(
    name: "DefaultAreaApi",
    routeTemplate: "api/{area}/{controller}/{id}",
    defaults: new {id = RouteParameter.Optional}
    );

    routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
    );


    ReplyDelete
    Replies
    1. Could you try to create the API controllers with the same names in the different areas? ;)

      Delete
    2. I see. It wasn't clear to me that the problem this post aims to solve is this exception:

      Multiple types were found that match the controller named 'blah'. This can happen if the route that services this request ('services/{area}/{controller}/{id}') found multiple controllers defined with the same name but differing namespaces, which is not supported. The request for 'blah' has found the following matching controllers: MyProject.Web.Areas.Whatever.Controllers.BlahController MyProject.Web.Areas.Whatever2.Controllers.BlahController

      Makes sense now and works great. Thanks for the post and reply! ;)

      Delete
    3. Another question... It seems if I define the route in the global routes as api/{area}/{controller} it works. If I define the route as either {area}/api/{controller} or api/{area}/{controller} (flipping api and area) in each [Controller]AreaRegistration class it works either way. But if I flip it in the global route and try to define it as {area}/api/{controller} it doesn't work. Any thoughts on why this doesn't work (or are you able to do this)?

      Another caveat that I found is that you still can't have a controller at the root with the same name as a controller in an area. Is that correct?

      Thanks!

      Delete
    4. > Another caveat that I found is that you still can't have a controller at the root with the same name as a controller in an area. Is that correct?
      Yes, it's correct.

      > But if I flip it in the global route and try to define it as {area}/api/{controller} it doesn't work
      sorry, but I can't say exactly why it does not work now. I should check.

      Delete
  6. Thanks for you posting, but seems I can't find a controller by search ".{area}." with full controller class name.

    I'm using the self-host Web API, after loaded all controller types, the sample controller fullname with format SelfHost.ProductsController, even I already add area while config the MapHttpRoute.

    Am I missing any steps, or this way don't support self host Web API?

    ReplyDelete
  7. Ohh! Saved my life! thank you for an amazng fix.

    ReplyDelete
  8. Wow! thanks! this saved me a lot of agony. But I still have a problem. The automatic help generator generates other items that are not there because of the routing. I have the routes
    api/admin/{controller}/{id}
    api/{area}/{controller}/{id}
    I have controllers MyProject.Web.Admin.AccountsController and MyProject.Web.NewsController
    now in the documentation I get, options like
    GET api/admin/accounts
    GET api/admin/news
    GET api/accounts
    GET api/news
    Obviously that throws an exception. Any idea of how to solve that

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. This is really nice. Thanks for sharing this article
    Dot net training Chennai

    ReplyDelete
  11. Thanks for sharing this useful information..Its really very informative.

    Dot Net Course Chennai

    ReplyDelete
  12. Thanks for sharing informative post about Microsoft Visual Studio. This platform is used to create web application and services. Being widely used software framework, this domain offer huge career opportunity for trained professionals. We at, DOT NET Training offer hands on training in this evergreen technology.

    ReplyDelete
  13. I get a lot of great information from this blog. Thank you for your sharing this informative blog. I have bookmarked this page for my future reference. Recently I did oracle certification course at a leading academy. If you are looking for best Oracle Training in Chennai visit FITA IT training and placement academy which offer PL SQL Training in Chennai.

    ReplyDelete
  14. Thanks for sharing these niche piece of coding to our knowledge. Here, I had a solution for my inconclusive problems & it’s really helps me a lot keep updates…
    DOT NET Training in Chennai | Fita Chennai Reviews

    ReplyDelete


  15. Thanks for sharing this informative blog. FITA provides Salesforce Course in Chennai with years of experienced professionals and fully hands-on classes. Salesforce is a cloud based CRM software. Today's most of the IT industry use this software for customer relationship management. To know more details about salesforce reach FITA Academy. Rated as No.1 Salesforce Training Institutes in Chennai.

    ReplyDelete
  16. SAP Training

    Thanks for sharing this valuable information.and I gathered some information from this blog. I did SAP Course in Chennai, at FITA Academy which offer best SAP Training in Chennai with years of experienced professionals.

    SAP Training Institute in Chennai

    ReplyDelete
  17. I have read your blog and i got a very useful and knowledgeable information from your blog.You have done a great job . If anyone want to get Salesforce Training in Chennai, Please visit FITA academy located at Chennai Velachery. Rated as No.1 Salesforce Training Institutes in Chennai.

    ReplyDelete
  18. Thanks for sharing this valuable information to our vision. You have posted a trust worthy blog keep sharing.

    html5 Training in chennai | html5 Training chennai

    ReplyDelete
  19. Your posts is really helpful for me.Thanks for your wonderful post. I am very happy to read your post.

    Mysql Training in chennai | Mysql Training chennai

    ReplyDelete
  20. There are lots of information about latest technology and how to get trained in them, like Big Data Training in Chennai have spread around the web, but this is a unique one according to me. The strategy you have updated here will make me to get trained in future technologies(Big Data Training). By the way you are running a great blog. Thanks for sharing this. cloud computing training in chennai

    ReplyDelete
  21. Oracle DBA Training in Chennai
    Thanks for sharing this informative blog. I did Oracle DBA Certification in Greens Technology at Adyar. This is really useful for me to make a bright career..

    ReplyDelete
  22. Whatever we gathered information from the blogs, we should implement that in practically then only we can understand that exact thing clearly, but it’s no need to do it, because you have explained the concepts very well. It was crystal clear, keep sharing..
    Websphere Training in Chennai

    ReplyDelete
  23. Data warehousing Training in Chennai
    I am reading your post from the beginning, it was so interesting to read & I feel thanks to you for posting such a good blog, keep updates regularly..

    ReplyDelete
  24. Selenium Training in Chennai
    Wonderful blog.. Thanks for sharing informative blog.. its very useful to me..

    ReplyDelete
  25. Oracle Training in chennai
    Thanks for sharing such a great information..Its really nice and informative..

    ReplyDelete
  26. I have read your blog and i got a very useful and knowledgeable information from your blog.You have done a great job.
    SAP Training in Chennai

    ReplyDelete
  27. This information is impressive..I am inspired with your post writing style & how continuously you describe this topic. After reading your post,thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic
    Android Training In Chennai In Chennai

    ReplyDelete
  28. Pretty article! I found some useful information in your blog, it was awesome to read,thanks for sharing this great content to my vision, keep sharing..
    Unix Training In Chennai

    ReplyDelete
  29. I found some useful information in your blog, it was awesome to read, thanks for sharing this great content to my vision, keep sharing..
    SalesForce Training in Chennai

    ReplyDelete
  30. There are lots of information about latest technology and how to get trained in them, like Best Hadoop Training In Chennai have spread around the web, but this is a unique one according to me. The strategy you have updated here will make me to get trained in future technologies Hadoop Training in Chennai By the way you are running a great blog. Thanks for sharing this blogs..

    ReplyDelete
  31. Wow! It was the best article , actually you have posted something new compared to others, because I read many articles related to this topic but I only get impressed with your post only, keep posting.
    Regards,
    Informatica training in chennai|Best Informatica Training In Chennai|Informatica course in Chennai

    ReplyDelete
  32. This information is impressive; I am inspired with your post writing style & how continuously you describe this topic. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic..
    Informatica Training in chennai | QTP Training in Chennai



    ReplyDelete
  33. This information is impressive; I am inspired with your post writing style & how continuously you describe this topic. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic..
    Informatica Training in chennai | QTP Training in Chennai



    ReplyDelete
  34. This information is impressive; I am inspired with your post writing style & how continuously you describe this topic. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic..
    Selenium Training in Chennai | QTP Training in Chennai

    ReplyDelete
  35. Thanks for Information Oracle Apps Technical is a collection of a bunch of collected applications like accounts payables, purchasing, inventory, accounts receivables, human resources, order management, general ledger and fixed assets, etc which have its own functionality for serving the business
    Oracle Apps Training In Chennai

    ReplyDelete
  36. Oracle Training in chennai | Oracle D2K Training In chennai
    This information is impressive; I am inspired with your post writing style & how continuously you describe this topic. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic..

    ReplyDelete
  37. It’s too informative blog and I am getting conglomerations of info’s. Thanks for sharing; I would like to see your updates regularly so keep blogging. If anyone looking car just get here
    Regards,
    sas training in Chennai|sas course in Chennai|sas institutes in Chennai

    ReplyDelete
  38. Pretty Post! It is really interesting to read from the beginning & I would like to share your blog to my circles for getting awesome knowledge, keep your blog as updated.
    Regards,
    Oracle Training in Chennai|Oracle DBA Training in Chennai|Oracle Training Institutes in Chennai

    ReplyDelete
  39. Thanks for sharing this informative blog. If you are interested in taking .net in professional carrier visit this website.Dot Net Training in Chennai

    ReplyDelete
  40. Really awesome blog. Your blog is really useful for me. Thanks for sharing this informative blog. Keep update your blog.
    Oracle Training In Chennai

    ReplyDelete
  41. Best SAS Training Institute In Chennai It’s too informative blog and I am getting conglomerations of info’s about Oracle interview questions and answer .Thanks for sharing, I would like to see your updates regularly so keep blogging.

    ReplyDelete
  42. I am not getting this much information in any other blogs, Thanks admin for giving detail stuffs of MI am not getting this much information about Cloud computing in any other blogs, Thanks admin for giving detail stuffs of MVC.
    Regards,
    Cloud computing course in Chennai|cloud training in chennai.

    ReplyDelete
  43. Phone calls can be composed so that the calling party calls alternate members and adds them to the call; be that as it may, members are generally ready to call into the telephone call themselves by dialing a phone number that interfaces with a "meeting extension" (a specific sort of hardware that connections phone lines).Conference Calling Via Phone

    ReplyDelete
  44. Well Said, you have furnished the right information that will be useful to anyone at all time. Thanks for sharing your Ideas.
    oracle training in chennai | oracle training institutes in chennai

    ReplyDelete
  45. for preparing bank exam and group exam , we are providing an online test model questions papers

    Group Exam Questions and Answers
    Bank Exam Questions and Answers

    ReplyDelete
  46. Tremendous information, i had come to recognise about your blog from my buddy nandu , hyderaba.
    I have read atleast 7 posts of yours through now, and let me inform you, your website gives the
    Great and the maximum interesting records. that is just the form of statistics that i had
    Been searching out, i'm already your rss reader now and i'd frequently be careful for the brand new posts.
    from
    oracle cloud fusion financials

    ReplyDelete
  47. I get a lot of great information from this blog.
    Thank you for your sharing this informative blog.
    I have bookmarked this page for my future reference.
    Recently I did oracle certification course at a leading academy.
    If you are looking for best Oracle Training in Chennai visit http://call360.in/Chennai/educational_institutes_greens_technology_adyar_adyar.php training
    and placement academy which offer PL SQL Training in Chennai.

    ReplyDelete
  48. Fastest local business directory in India CALL360

    ReplyDelete
  49. GREEN WOMEN HOSTELGreen Women hostel is one of the leading Ladies hostel in Adyar and we serving an excellent service to Staying people, We create a home atmosphere, it is the best place for Working WomenOur hostel Surrounded around bus depot, hospital, atm, bank, medical Shop & 24 hours Security Facility

    ReplyDelete
  50. https://shilohjanee.blogspot.in/2015/08/working-women-hostel-in-chennai_17.html?showComment=1492595069170#c8266620196316480802

    ReplyDelete

  51. This is excellent information. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me...
    Android Training in Chennai
    Ios Training in Chennai

    ReplyDelete
  52. This is a great article, I have been always to read something with specific tips! I will have to work on the time for scheduling my learning.
    Dot Net Training in Chennai

    ReplyDelete
  53. Very good idea you've shared here, from here I can be a very valuable new experience.
    Java Training in Chennai
    Java Training Institute in Chennai

    ReplyDelete
  54. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me...
    Android Training in Chennai
    Ios Training in Chennai

    ReplyDelete
  55. That was an educative piece of article on Support Areas in HttpControllerSelector and I have understood a lot of facts and principles in addition to the shared programs. Thanks for sharing with us such an informative and educative post. I will be recommending it to our professional editors who offer editing services to individuals who Need Citation Auditing Help.

    ReplyDelete
  56. I like your writing style, it was very clear to understanding the concept well; I hope you ll keep your blog as updated.
    Regards,
    Hadoop Training Chennai|Hadoop Training in Chennai

    ReplyDelete
  57. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me...
    Android Training in Chennai
    Ios Training in Chennai

    ReplyDelete