Create your own Asp. Net Web Api Route and apiroute that support version iterations

Source: Internet
Author: User

Create your own Asp. Net Web Api Route and apiroute that support version iterations

In the current mainstream architecture, we are increasingly seeing the existence of web APIs, which are small, flexible, and based on the Http protocol, make it act as a good service endpoint in more and more microservice projects or mobile projects.

Asp. for example, with the expansion of services and product iteration, our Web APIs also change. In many cases, multiple versions coexist, in this case, we need to design a web api link that supports the version number, for example:

Originally: http://www.test.com/api/?controller=/#id}

Today: http://www.test.com/api/?version=/?controller=/#id}

When we first designed it, we may not consider the version. I saw that many projects will add a "? Version = ", this method does solve the problem, but for Asp. for the Net Web Api, the access is still the same Controller. We need to judge the version number in the same Action, for example:

Http://www.test.com/api/bolgs? Version = v2 [HttpGet]

public class BlogsController : ApiController{    // GET api/<controller>    public IEnumerable<string> Get([FromUri]string version = "")    {        if (!String.IsNullOrEmpty(version))        {            return new string[] { $"{version} blog1", $"{version} blog2" };        }        return new string[] { "blog1", "blog2" };    }}

We can see that we return corresponding results by judging the version parameter in the url. To ensure the availability of the original interface, we need to assign the default value to the parameter, although it can solve our version iteration problem, however, as the version is constantly updated, you will find that the Controller will become more and more bloated and the maintenance will become more and more difficult, because this modification has seriously violated the OCP (Open-Closed Principle ), the best way is not to modify the original Controller, but to create a new Controller and put it in the corresponding directory (or project), for example:

In order not to affect the original project, we should try not to change the Namespace of the original Controller. unless you are sure that it is not affected, please try to move it to the directory.

OK. To maintain the ing of the original interface, we need to Register the Route ing that supports the version number in WebApiConfig. Register:

config.Routes.MapHttpRoute(    name: "DefaultVersionApi",    routeTemplate: "api/{version}/{controller}/{id}",    defaults: new { id = RouteParameter.Optional });

Open your browser or postman and enter the original api url. You will find this error:

This is because when the web api searches for the Controller, it only performs the search based on the ClassName. When the same ClassName occurs, this error is reported. At this time, we need to create our own Controller Selector, fortunately, Microsoft left an interface for us: IHttpControllerSelector. However, in order to be compatible with the original api (Some APIs that are not within our permission range and do not include the version number), it is better to directly integrate DefaultHttpControllerSelector. We give a rule, instead of taking charge of the version iteration api, Let it go through the original ing.

Ideas

1. When the project is started, add the qualified Controller to a dictionary.

2. If the request is determined to comply with the rules, we will return our controller.

Create your own Selector

Now that we have some ideas, we can make the transformation very simple. Today we will first make a simple one, and wait for time to change it to configurable.

Step 1: Create a Selector class, inherit from DefaultHttpControllerSelector, and create a dictionary of our own during initialization:

Public class VersionHttpControllerSelector: extends {private readonly HttpConfiguration _ configuration; private readonly Lazy <Dictionary <string, HttpControllerDescriptor> _ lazyMappingDictionary; private const string DefaultVersion = "v1"; // default version, because the previous api does not have the concept of version number private const string DefaultNamespaces = "WebApiVersions. controllers "; // a namespace private const strin is used for demonstration convenience. G RouteVersionKey = "version"; // The Version string private const string DictKeyFormat = "{0} in the routing rule }. {1} "; public VersionHttpControllerSelector (HttpConfiguration configuration): base (configuration) {_ configuration = configuration; _ lazyMappingDictionary = new Lazy <Dictionary <string, HttpControllerDescriptor> (InitializeControllerDict );} private Dictionary <string, HttpControllerDescriptor> InitializeContro LlerDict () {var result = new Dictionary <string, HttpControllerDescriptor> (StringComparer. ordinalIgnoreCase); var assemblies = _ configuration. services. getAssembliesResolver (); var controllerResolver = _ configuration. services. getHttpControllerTypeResolver (); var controllerTypes = controllerResolver. getControllerTypes (assemblies); foreach (var t in controllerTypes) {if (t. namespace. contains (DefaultN Amespaces) // complies with NameSpace rules {var segments = t. Namespace. Split (Type. Delimiter); var version = t. Namespace. Equals (DefaultNamespaces, StringComparison. OrdinalIgnoreCase )? DefaultVersion: segments [segments. length-1]; var controllerName = t. name. remove (t. name. length-DefaultHttpControllerSelector. controllerSuffix. length); var key = string. format (DictKeyFormat, version, controllerName); if (! Result. ContainsKey (key) {result. Add (key, new HttpControllerDescriptor (_ configuration, t. Name, t) ;}} return result ;}}


With the dictionary, we can do it now. We only need to analyze the request. If the request meets our version requirements, we can find the corresponding Descriptor from our dictionary. If not, the default value is used. here we need to override the SelectController method:

Public override HttpControllerDescriptor SelectController (HttpRequestMessage request) {IHttpRouteData routeData = request. getRouteData (); if (routeData = null) throw new HttpResponseException (HttpStatusCode. notFound); var controllerName = GetControllerName (request); if (String. isNullOrEmpty (controllerName) throw new HttpResponseException (HttpStatusCode. notFound); var version = DefaultVersion; if (IsVersionRoute (routeData, out version) {var key = String. format (DictKeyFormat, version, controllerName); if (_ lazyMappingDictionary. value. containsKey (key) {return _ lazyMappingDictionary. value [key];} throw new HttpResponseException (HttpStatusCode. notFound);} return base. selectController (request);} private bool IsVersionRoute (IHttpRouteData routeData, out string version) {version = String. empty; var prevRouteTemplate = "api/{controller}/{id}"; object outVersion; if (routeData. values. tryGetValue (RouteVersionKey, out outVersion) // first find the route version that complies with the new rule {version = outVersion. toString (); return true;} if (routeData. route. routeTemplate. contains (prevRouteTemplate) // check whether the original api route conforms to {version = DefaultVersion; return true;} return false ;}

After this class is completed, go to WebApiConfig. Register to replace the class:

config.Services.Replace(typeof(IHttpControllerSelector), new VersionHttpControllerSelector(config));

OK, open the browser again, enter http://www.xxx.com/api/blogs and http://www.xxx.com/api/v2/blogs, then you should be able to see the correct execution:

Conclusion

Today, we have created a ControllerSelector that simply conforms to the webapi version update iteration, but it is not perfect, because many of them are hard code. I will create a ControllerSelector that supports configuration and put it on github.

I have been studying eShopOnContrainers for a long time, and I have been studying eShopOnContrainers recently. However, I am a little busy with my work. Sorry, if you have any questions about. Net or want to make friends with technologies, you can add the QQ group: 376248054.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.