{"id":274,"date":"2014-08-22T10:48:00","date_gmt":"2014-08-22T10:48:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/2014\/08\/22\/customizing-web-api-controller-selector\/"},"modified":"2014-08-22T10:48:00","modified_gmt":"2014-08-22T10:48:00","slug":"customizing-web-api-controller-selector","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/customizing-web-api-controller-selector\/","title":{"rendered":"Customizing Web API controller selector"},"content":{"rendered":"<p>In the last few weeks we have encountered a customer reports of performance issues with their Web API applications. <\/p>\n<p>When the 4th report exhibited the same phenomenon I figured it\u2019s time to blog about it. <\/p>\n<h3>Why would you customize a controller selector? (hint \u2013 try not to)<\/h3>\n<p>It gives you a chance to control how the request gets routed. By default Web API uses the controller token from routing to select the controller. If you want a different behavior, such as selection based on an HTTP header, then you need to customize the controller selector. <\/p>\n<p>Wait. Not really. This was true for Web API 1.0. In Web API 2.0 <a href=\"http:\/\/www.asp.net\/web-api\/overview\/web-api-routing-and-actions\/attribute-routing-in-web-api-2\">attribute routing<\/a> and inline constraints were added, with further enhancements in <a>Web API <\/a><a href=\"http:\/\/www.asp.net\/web-api\/overview\/releases\/whats-new-in-aspnet-web-api-21\">2.1<\/a> and <a href=\"http:\/\/www.asp.net\/web-api\/overview\/releases\/whats-new-in-aspnet-web-api-22\">2.2<\/a> <\/a>. Many of the scenarios once handled only by custom controller selectors could now be implemented using features of attribute routing. <\/p>\n<p>Attribute routing allows you to use declarative templates and custom constraints directly on actions and controllers. This means that you are applying custom behavior in a few select places instead of affecting the behavior of the entire application. If instead you need to apply a custom behavior to all of your actions, attribute routing allows you to provide custom route discovery code. They still apply as normal attribute routes, so the rest of the system keeps functioning as default. The <a href=\"http:\/\/www.asp.net\/web-api\/overview\/releases\/whats-new-in-aspnet-web-api-22#ARI\">release notes for Web API 2.2<\/a> demonstrate a custom <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.http.routing.idirectrouteprovider(v=vs.118).aspx\">IDirectRouteProvider<\/a>, which is used to customize attribute routes. <\/p>\n<p>Most importantly, attribute routing doesn\u2019t replace a core framework component so you don\u2019t need to worry about concerns such as caching descriptors and API Explorer compatibility. <\/p>\n<p>Here are some further samples for attribute routing: <\/p>\n<p><a href=\"http:\/\/aspnet.codeplex.com\/SourceControl\/latest#Samples\/WebApi\/WebApiAttributeRoutingSample\/Readme.txt\">Versioning with attribute routing<\/a> <\/p>\n<p><a href=\"http:\/\/aspnet.codeplex.com\/SourceControl\/latest#Samples\/WebApi\/WebApiAttributeRoutingSample\/Readme.txt\">Attribute Routing<\/a> <\/p>\n<h3>Back to IControllerSelector<\/h3>\n<p>I have seen developers use it to enable multiple controllers with the same type name but that are in different namespaces. I have seen it used for versioning controllers based on extra data from the request, such as in an HTTP header. Changing the controller selector for the above scenarios works well for the purpose of action selection, but might not work as well when you try to use the Web API help page or link generation. In your application that might be good enough for your purposes. <\/p>\n<p>So say you still want to use conventional (non-attribute) routing and customize the controller selector. Go ahead, but watch out for the following problems. <\/p>\n<h3>What to watch out for<\/h3>\n<p>Let\u2019s start by taking a look at the interface you will implement:<\/p>\n<p><span style=\"background: white;color: gray\"><\/span><\/p>\n<pre class=\"code\"><span style=\"background: white;color: blue\">public interface <\/span><span style=\"background: white;color: #2b91af\">IHttpControllerSelector\n<\/span><span style=\"background: white;color: black\">{\n    <\/span><span style=\"background: white;color: #2b91af\">HttpControllerDescriptor <\/span><span style=\"background: white;color: black\">SelectController(<\/span><span style=\"background: white;color: #2b91af\">HttpRequestMessage <\/span><span style=\"background: white;color: black\">request);\n    <\/span><span style=\"background: white;color: #2b91af\">IDictionary<\/span><span style=\"background: white;color: black\">&lt;<\/span><span style=\"background: white;color: blue\">string<\/span><span style=\"background: white;color: black\">, <\/span><span style=\"background: white;color: #2b91af\">HttpControllerDescriptor<\/span><span style=\"background: white;color: black\">&gt; GetControllerMapping();\n}<\/span><\/pre>\n<p>Take a look at GetControllerMapping. The controller mapping says that a controller is mapped to a name. There is no way to specify the namespace or any more information, so if you are going to make controller selection decisions based on its namespace (like in the sample below), note that ApiExplorer is not aware of controllers with the same name but in different namespaces. <\/p>\n<p>Next is the SelectController method. It returns an HttpControllerDescriptor, which seems innocent, but if you read through the constructor of that class, you will see that it calls Initialize() and then reflects over the controller attributes:<\/p>\n<pre class=\"code\"><span style=\"background: white;color: blue\"><font color=\"#0000ff\">object<\/font><\/span><span style=\"background: white;color: black\">[] attrs = type.GetCustomAttributes(inherit: <\/span><span style=\"background: white;color: blue\">false<\/span><span style=\"background: white;color: black\">);<\/span><\/pre>\n<p>And then initializes a new controller settings, and initializes a new configuration. <\/p>\n<p><strong>Still doesn\u2019t sound too bad?<\/strong> <\/p>\n<p>With the default controller selector, this will happen just once because the descriptor is cached, but with a na\u00efve customization this could happen on <i>every<\/i> request. This logic is relatively expensive, including many memory allocations, and a significant performance degradation can be measured. In some cases we have seen web services that could easily serve around 3000 requests per second, go down to less than a 1,000 just because of such a change. <\/p>\n<p>One other thing to note is that calling the default constructor for HttpControllerDescriptor avoids instantiating the configuration, and can leave your application into an unexpected state. This constructor is marked in the documentation that it is for <b>unit tests only <\/b>and must not be used in production code. <\/p>\n<h3>So what to do<\/h3>\n<p>If all you are doing is customizing the controller name, just derive from <span style=\"background: white;color: #2b91af\">DefaultHttpControllerSelector <\/span>and override GetControllerName. The default controller selector will take care of caching the descriptor. <\/p>\n<p>If you are implementing something more elaborate and you want to create your own descriptor be aware that you need to implement a caching mechanism, as the default implementation will not be sufficient. <\/p>\n<h3>Sample<\/h3>\n<p>Here is a sample that properly deals with controller selection (if not the side effects on API Explorer) and properly caches the controller descriptors: <\/p>\n<p><a title=\"http:\/\/aspnet.codeplex.com\/SourceControl\/latest#Samples\/WebApi\/NamespaceControllerSelector\/NamespaceHttpControllerSelector.cs\" href=\"http:\/\/aspnet.codeplex.com\/SourceControl\/latest#Samples\/WebApi\/NamespaceControllerSelector\/NamespaceHttpControllerSelector.cs\">http:\/\/aspnet.codeplex.com\/SourceControl\/latest#Samples\/WebApi\/NamespaceControllerSelector\/NamespaceHttpControllerSelector.cs<\/a><\/p>\n<h3>Conclusion<\/h3>\n<p><span style=\"background: white;color: #2b91af\"><font color=\"#000000\">Implementing an<\/font> IHttpControllerSelector <\/span>is something you don\u2019t want to opt into lightly. But if you do, please consider caching and effects on other parts of the system.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the last few weeks we have encountered a customer reports of performance issues with their Web API applications. When the 4th report exhibited the same phenomenon I figured it\u2019s time to blog about it. Why would you customize a controller selector? (hint \u2013 try not to) It gives you a chance to control how [&hellip;]<\/p>\n","protected":false},"author":407,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197],"tags":[7436,7230],"class_list":["post-274","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","tag-visual-studio-2013","tag-web-api"],"acf":[],"blog_post_summary":"<p>In the last few weeks we have encountered a customer reports of performance issues with their Web API applications. When the 4th report exhibited the same phenomenon I figured it\u2019s time to blog about it. Why would you customize a controller selector? (hint \u2013 try not to) It gives you a chance to control how [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/274","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/407"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=274"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/274\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=274"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=274"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=274"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}