Original: Webapi add area to support unlimited level controller with the same name
Microsoft's WEBAPI default implementation logic
A controller with the same name is not supported in the default implementation, otherwise it will be reported httperror on the Internet, and various routing self-implementations are found on the web.
Controller classification for ASP.
Set of problems encountered when building MVC and WEBAPI project frameworks
With the help of the above address, according to the requirements, rewrite the areahttpcontrollerselector, the routing principle and the above address is similar, are through the route matching stitching FullName, and then match the closest apicontroller, and the so-called closest, This means that if more than one match is obtained based on the name of the stitching, get the apicontroller with the fewest number of namespace nodes to ensure that the routing rules are registered multiple times. The controller can be matched in a way from complex to simple (it is important to note that Areahttpcontrollerselector is the controller as the end of the split point), for example:
Assume that the following route matching rules are registered (the controller, the webapi of the action, and the character of the route)
Config. Routes.maphttproute ( name: "Defaultareaapi", routetemplate: "Api/{area}/{controller}/{action}/{id}", defaults:new {id = routeparameter.optional} ); Config. Routes.maphttproute ( name: "Defaultapi", routetemplate: "Api/{controller}/{action}/{id}", defaults: New {id = routeparameter.optional} );
In the controller directory, there are multiple layers of controllers with the same name and different levels, such as:
Controller/area/samecontroller, the corresponding namespace is Controller.Area.SameController
Controller/samecontroller, the corresponding namespace is Controller.samecontroller
By Api/area/same/get will match to Controller/area/samecontroller
By Api/same/get will match to Controller/samecontroller
The rewritten areahttpcontrollerselector can support an infinite hierarchy of areas, as long as the namespace is supported, as opposed to the reference URL, such as
"Api/{area1}/{area1}/{area2}/{area3}/{controller}/{action}/{id}"
The following is the specific Areahttpcontrollerselector code
Using system;using system.collections.generic;using system.linq;using system.text;using System.Threading.Tasks; Using system.web;using system.web.http.dispatcher;using system.net.http;using system.web.http;using System.web.http.controllers;using system.net;namespace webapi{///<summary>//Represents a area system.web. Http.Dispatcher.IHttpControllerSelector instance//</summary> public class Areahttpcontrollerselector:defa Ulthttpcontrollerselector {private readonly httpconfiguration _configuration; <summary>///Lazy all Ihttpcontroller reflection collection contained in the current assembly, TKey for lowercase controllers///</summary> P Rivate readonly lazy<ilookup<string, type>> _apicontrollertypes; Private ilookup<string, type> apicontrollertypes {get {return This._ap Icontrollertypes.value; }}///<summary>//Initializes a new instance of the ISAhttpcontrollerselector class///</summary>//<param name= "Configuration" ></param> Public Areahttpcontrollerselector (httpconfiguration configuration): Base (configuration) { this._configuration = Configuration; This._apicontrollertypes = new lazy<ilookup<string, type>> (this. Getapicontrollertypes); }///<summary>//Get current assembly Ihttpcontroller reflection Collection///</summary>//<returns> ;</returns> private ilookup<string, type> getapicontrollertypes () {Iassembliesresolv Er assembliesresolver = this._configuration. Services.getassembliesresolver (); Return this._configuration. Services.gethttpcontrollertyperesolver (). Getcontrollertypes (Assembliesresolver). ToLookup (t = T.name.tolower (). Substring (0, t.name.length-defaulthttpcontrollerselector.controllersuffix.length), t = = t); }//<summary>//Selects a System.Web.Http.Controllers.HttpControllerDescriptor for the given Syste M.net.http.httprequestmessage. </summary>//<param name= "Request" ></param>//<returns></returns> public override Httpcontrollerdescriptor Selectcontroller (Httprequestmessage request) {Httpcontrolle Rdescriptor des = null; String controllername = this. Getcontrollername (Request); if (!string. Isnullorwhitespace (Controllername)) {var groups = this. Apicontrollertypes[controllername.tolower ()]; if (groups! = null && groups. Any ()) {string endstring; var routedic = Request. Getroutedata (). values;//existence controllername words must be able to take ihttproutedata if (Routedic.count > 1) { StringBuilder tmp = new StringbuildeR (); foreach (var key in Routedic.keys) {tmp. Append ('. '); Tmp. Append (Routedic[key]); if (key. Equals (Defaulthttpcontrollerselector.controllersuffix, Stringcomparison.currentcultureignorecase)) {//If control, represents the end of the namespace break; }} tmp. Append (Defaulthttpcontrollerselector.controllersuffix); endstring = tmp. ToString (); } else {endstring = string. Format (". { 0}{1} ", Controllername, Defaulthttpcontrollerselector.controllersuffix); }//Take the minimum number of namespace nodes type var type = groups. Where (t = T.fullname.endswith (endstring, Stringcomparison.currentcultureignorecase)). (t = = T.fullname.count (s = = = ')). FirstOrDefault ()///default returns the least number of namespace nodes for the first if (type! = null) {des = new Httpcontrollerdescriptor (this._configuration, controllername, type); }}} if (des = = null) {throw new httpresponseexception (re Quest. Createerrorresponse (Httpstatuscode.notfound, String. Format ("No route providing a controller name is found to match request URI ' {0} '", request.) (RequestUri))); } return des; } }}
The usage is to replace the registration in the Application_Start method of the global file
GlobalConfiguration.Configuration.Services.Replace (typeof (Ihttpcontrollerselector), new Areahttpcontrollerselector ( globalconfiguration.configuration));
Webapi add area to support unlimited levels of controller with the same name