Versioning helps in rolling out features on a timely basis, without breaking the existing system. It can also help to provide additional functionalities to selected customers. API versioning can be done in different ways like appending the version in the URL or as a query string parameter, via custom header and via Accept-Header. In this post, let’s find how to support multiple version ASP.NET Core Web API.
Support multiple versions of ASP.NET Core Web API
Let’s create an ASP.NET Core web API application. Open Project.json and include following nuget package.
"Microsoft.AspNetCore.Mvc.Versioning": "1.0.3"
Once the package is restored, we need to configure it. Next open Startup.cs, add the highlighted lines of code in ConfigureServices
method.
public void ConfigureServices(IServiceCollection services) { services.AddApplicationInsightsTelemetry(Configuration); services.AddMvc(); services.AddApiVersioning(option => { option.ReportApiVersions = true; option.AssumeDefaultVersionWhenUnspecified = true; option.DefaultApiVersion = new ApiVersion(1, 0); }); }
As you can see, there are 3 different options configured.
- ReportAPIVersions: This is optional. But when set to true, API returns supported versions information in the response header.
- AssumeDefaultVersionWhenUnspecified: This option will be used to serve the request without a version. The assumed API version by default would be 1.0.
- DefaultApiVersion: This option is used to specify the default API version to be used when no version is specified in the request. This will default the version to 1.0.
That’s all for the configuration and setup. Now we will see different ways of accessing the versions of the API.
Via Query String
Open the controller class, then decorate the controller class with ApiVersion
attribute. Like,
namespace MultipleAPIVersions.Controllers { [ApiVersion("1.0")] [Route("api/[controller]")] public class ValuesController : Controller { [HttpGet] public IActionResult Get() => Ok(new string[] { "value1" }); } }
Here, the version is mentioned as 1.0. You can also create another controller class with the same name in a different namespace with API version set to 2.0. Like,
namespace AspNetCoreWebApi.Controllers2 { [ApiVersion("2.0")] [Route("api/[controller]")] public class ValuesController : Controller { [HttpGet] public IActionResult Get() => Ok(new string[] { "value2" }); } }
That’s all. Now go to browser and access the controller. You should see the output from API version 1.0 controller as it is set to default. Now append ?api-version=2 in the URL and you should see output from API version 2.0 controller.
Via URL Path Segment
Query string parameters are useful, but it can be painful in case of long URL and other query string parameters. Instead, the better approach would be to add version in the URL path. Like,
- api/v1/values
- api/v2/values
So to do this, we need to put the version in the route attribute. Like,
namespace MultipleAPIVersions.Controllers { [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] public class ValuesController : Controller { [HttpGet] public IActionResult Get() => Ok(new string[] { "value1" }); } }
Similarly, you will need to update route parameters to at all applicable places. With this change, the API endpoints always need to have the version number. You can navigate to version 1.0 via api/v1/values
and to access version 2.0, change the version number in the URL. Simple and looks more clean now.
Via HTTP Headers
In both the above approaches, the URL needs to be modified to support versioning. But if you want your API URL’s to stay clean, then the API version information can also be passed via appending an HTTP Header. For this to work, you need to configure ApiVersionReader
option.
services.AddApiVersioning(option => { option.ReportApiVersions = true; option.ApiVersionReader = new HeaderApiVersionReader("api-version"); option.DefaultApiVersion = new ApiVersion(1, 0); option.AssumeDefaultVersionWhenUnspecified = true; });
The highlighted line tells that header “api-version” is now where the API version number is expected. Make sure that Route
attribute didn’t have version details. So test it, open Postman to add the header.
When you supply 2.0 as value to “api-version”, it calls the Version 2.0 controller and returns the output.
Simple and easy to set up. However, now the query string parameter will not work. Once you set the header, you can’t specify a query string parameter. If you wish to support both then instead of HeaderApiVersionReader
, use QueryStringOrHeaderApiVersionReader
. Like,
option.ApiVersionReader = new QueryStringOrHeaderApiVersionReader() { HeaderNames = { "api-version", "x-ms-version" } };
So now, both query string parameter and header are supported. The default query string parameter name is api-version, so you can leave the constructor empty, but if you want another name, then you will need to supply. You can also have different names for query string parameter and for the header.
option.ApiVersionReader = new QueryStringOrHeaderApiVersionReader(parameterName: "version") { HeaderNames = { "api-version", "x-ms-version" } };
Remember, we also set ReportApiVersions
to true which returns versions information in the response header. See below image.
Now, let’s take a look at couple of another options.
MapToApiVersion
MapToApiVersion
attribute allows to map a single API action to any version. In other words, a single controller which supports multiple versions say 1 and 3. The controller may have an API action method supported by version 3 only. In such case, you can use MapToApiVersion
. Take a look at below code.
namespace MultipleAPIVersions.Controllers { [ApiVersion("1.0")] [ApiVersion("3.0")] [Route("api/v{version:apiVersion}/[controller]")] public class ValuesController : Controller { [HttpGet] public IActionResult Get() => Ok(new string[] { "value1" }); [HttpGet, MapToApiVersion("3.0")] public IActionResult GetV3() => Ok(new string[] { "value3" }); } }
Following is the output of all 3 versions.
Deprecated
When multiple API versions are supported, some versions will eventually be deprecated over time. To mark one or more API versions have been deprecated, simply decorate your controller with the deprecated API versions. This doesn’t mean that the API version is not supported. One can still call the endpoint/version. It just a way to make API users aware that following version will be deprecated in future.
[ApiVersion("1.0", Deprecated = true)]
Run the API and look into response header.
ApiVersionNeutral
ApiVersionNeutral
attribute defines that this API is version-neutral. This is useful for APIs that behaves the exact same way, regardless of API version or a legacy API that doesn’t support API versioning. So, you can add ApiVersionNeutral
attribute to opt out from versioning.
[ApiVersionNeutral] [RoutePrefix( "api/[controller]" )] public class SharedController : Controller { [HttpGet] public IActionResult Get() => Ok(); }
Access Version Information
If you wish to know which version client is trying to access, then you can get that information from,
public string Get() => HttpContext.GetRequestedApiVersion().ToString();
Summary
To sum up things, having multiple versions of API can help to roll out enhanced features in an effective way and it also makes easy to track the changes. In this post, we saw how to add support for multiple versions in ASP.NET Core WEB API. The nuget package supports versioning via query string parameters, adding path segment in the URL and via the headers. It also has options to deprecate versions, version a single API action and opting out from version.
Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section
My controllers in different namespaces but I have issue with ” Actions require unique method/path combination for Swagger”. How to resolve it?
thank u very much for posting this article.