受欢迎的博客标签

Support Multiple Versions Of ASP.NET Core Web API

Published
Now a days REST APIs are quite commonly used in web applications. At times you may need to provide multiple versions of your API to the clients. This could be because of enhanced functionality exposed by the API or it could for the sake of offering multiple sets of functionality (basic, advanced, free, paid etc.). This article discusses a few approaches for offering such multiple versions of your API to the client applications. If you are unfamiliar with creating and consuming Web API in ASP.NET Core, consider reading my articleshere, here, and here. Understanding the problem Suppose you have created a Web API in the form of CustomerController. You will typically access this API using the following URL: http://localhost:1234/api/customer So, the API clients will use this URL to invoke the Web API. Now assume that you wish to release an enhanced version of your Web API. For the sake of backward compatibility you don't want to touch the earlier API. One obvious solution could be create the new Controller (say, CustomerEnhanced) and publish it by different name. Say : http://localhost:1234/api/CustomerEnhanced But this means, for every new version you will keep inventing and publishing new API end points. It would be far better if you publish the APIs as multiple versions. For example : http://localhost:1234/api/Customer/v1.0 http://localhost:1234/api/Customer/v2.0 http://localhost:1234/api/Customer/v3.0 As you can see, the new scheme is much more meaningful than the earlier approach. Now version 1.0 API is accessible at the first URL whereas versions 2 and 3 are accessible at the second and third URL. The API name remains the same - Customer. But you publish multiple versions - 1.0, 2.0, and 3.0 - in this case. The older clients can continue to use the original URL they were built with and the newer clients can use the newer versions. The older clients can also switch to the newer versions if required. Quick and easy solution using API routing Let's first discuss a quick and easy way of dealing with the situation. This approach utilizes the attribute routing behavior of the Web API. It doesn't need any special NuGet package or component. The downside is it doesn't offer any discoverability of the versions to the consumers. We won't go into those details here. Let's go straight to some code. Consider the following code that shows three Web API controllers. namespace Version1 { [Route("api/[controller]/v1.0")] public class CustomerController : Controller { } } namespace Version2 { [Route("api/[controller]/v2.0")] public class CustomerController : Controller { } } namespace Version3 { [Route("api/[controller]/v3.0")] public class CustomerController : Controller { } } The above code consists of three namespaces - Version1, Version2, and Version3. Each of the namespaces define CustomerController that represents the API for the version under consideration. For the sake of simplicity the Web API actions are omitted. You will, of course, need them for the sake of testing. Notice the [Route] attributes added on top of each API controller classes. The route URL explicitly specifies the version number. So, http://localhost:1234/api/customer/v1.0 points to Version1.CustomerController and so on for the other URLs and the respective controllers. Open the browser and try making the GET request using the version 1.0 URL mentioned above. You will find that Get() action of Version1.CustomerController gets invoked. Try doing the same for other versions also. A better solution using [ApiVersion] attribute Although the above quick and easy approach works as expected it lacks features such as default version and version discoverability. That's where a better approach is needed. Luckily, there is a NuGet package that provides the help. Go ahead and add Microsoft.AspNetCore.Mvc.Versioning package to your project. Once the package is added, write the following code in the ConfigureServices() inside Startup class. public void ConfigureServices(IServiceCollection services) { ... services.AddApiVersioning(options => { options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; options.DefaultApiVersion = new ApiVersion(1, 0); }); } Here, we called the AddApiVersioning() method and also configured the services using certain properties such as ReportApiVersions, AssumeDefaultVersionWhenUnspecified, and  DefaultApiVersion. Although we won't discuss these properties in much detail, you can read more here. This package provides [ApiVersioning] attribute that can be used to specify API's version. The following code shows the modified API controllers with this attributed added. namespace Version1 { [ApiVersion("1.0")] [Route("api/[controller]/v{version:apiVersion}")] public class CustomerController : Controller { } } namespace Version2 { [ApiVersion("2.0")] [Route("api/[controller]/v{version:apiVersion}")] public class CustomerController : Controller { } } namespace Version3 { [ApiVersion("3.0")] [Route("api/[controller]/v{version:apiVersion}")] public class CustomerController : Controller { } } Notice the code marked in bold letters carefully. The [ApiVersion] attribute specifies the API's version. This version is added to the URL using a route parameter - v{version:apiVersion}. This way you don't need to hardcode the version in the route itself. If you invoke the API using the following URL : http://localhost:1234/api/customer/v1.0 then Version1.Customer will be invoked. Note that you don't need to always add the version to the route as shown above. If you decide to skip adding the version into the URL, you can also invoke a specific version using query string or HTTP headers. As an example, consider the following URL. http://localhost:49277/api/customer?api-version=1.0 In this case you specify the version number using api-version query string parameter. And your route will be : [ApiVersion("1.0")] [Route("api/[controller]")] public class CustomerController : Controller { ... } That's it for now! Keep coding !!.