When developing APIs, you should keep one thing in mind: Change is inevitable. When your API has reached a point where you need to add more responsibilities, you should consider versioning your API. Hence you will need a versioning strategy.
There are several approaches to versioning APIs, and each of them has its pros and cons. This article will discuss the challenges of API versioning and how you can work with Microsoft’s ASP.NET Core MVC Versioning package to version RESTful APIs built in ASP.NET Core. You can read more about Web API versioning in my previous article here.
Create an ASP.NET Core 3.1 API project
First off, let’s create an ASP.NET Core project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new ASP.NET Core project in Visual Studio.
- Launch the Visual Studio IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “ASP.NET Core Web Application” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window shown next, specify the name and location for the new project.
- Click Create.
- In the “Create New ASP.NET Core Web Application” window, select .NET Core as the runtime and ASP.NET Core 3.1 (or later) from the drop-down list at the top. I’ll be using ASP.NET Core 3.1 here.
- Select “API” as the project template to create a new ASP.NET Core API application.
- Ensure that the check boxes “Enable Docker Support” and “Configure for HTTPS” are unchecked as we won’t be using those features here.
- Ensure that Authentication is set as “No Authentication” as we won’t be using authentication either.
- Click Create.
This will create a new ASP.NET Core API project in Visual Studio. Select the Controllers solution folder in the Solution Explorer window and click “Add -> Controller…” to create a new controller named DefaultController.
Replace the source code of the DefaultController class with the following code.
[Route("api/[controller]")]
[ApiController]
public class DefaultController : ControllerBase
{
string[] authors = new string[]
{ "Joydip Kanjilal", "Steve Smith", "Stephen Jones" };
[HttpGet]
public IEnumerable<string> Get()
{
return authors;
}
}
We’ll use this controller in the subsequent sections of this article.
To implement API versioning in ASP.NET Core you need to do the following:
- Install the ASP.NET Core MVC Versioning package.
- Configure API versioning in the Startup class.
- Annotate the controllers and actions with appropriate attributes.
Install the ASP.NET Core MVC Versioning package
ASP.NET Core provides support for API versioning out-of-the-box. To leverage API versioning, all you need to do is install the Microsoft.AspNetCore.Mvc.Versioning package from NuGet. You can do this either via the NuGet package manager inside the Visual Studio 2019 IDE, or by executing the following command at the NuGet package manager console:
Install-Package Microsoft.AspNetCore.Mvc.Versioning
Note that if you’re using ASP.NET Web API, you should add the Microsoft.AspNet.WebApi.Versioning package.
Configure API versioning in ASP.NET Core
Now that the necessary package for versioning your API has been installed in your project, you can configure API versioning in the ConfigureServices method of the Startup class. The following code snippet illustrates how this can be achieved.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddApiVersioning();
}
When you make a Get request to your API, you will be presented with the error shown in Figure 1.
To solve this error, you can specify the default version when adding the API versioning services to the container. You might also want to use a default version if a version is not specified in the request. The following code snippet shows how you can set a default version as 1.0 using the AssumeDefaultVersionWhenUnspecified property if version information isn’t available in the request.
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
});
Note how the major version and minor version are passed to the constructor of the ApiVersion class at the time of assigning the default version. The property AssumeDefaultVersionWhenUnspecified can hold either true or false values. If it is true, the default version specified when configuring API versioning will be used if no version information is available.
The complete source code of the ConfigureServices method is given below for your reference.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
});
}
Since you haven’t specified any version information, all endpoints will have the default version 1.0.
Report all supported versions of your API
You might want to let the clients of the API know all supported versions. To do this, you should take advantage of the ReportApiVersions property as shown in the code snippet given below.
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
});
Use versions in the controller and action methods
Now let’s add a few supported versions to our controller using attributes as shown in the code snippet given below.
[Route("api/[controller]")]
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[ApiVersion("2.0")]
public class DefaultController : ControllerBase
{
string[] authors = new string[]
{ "Joydip Kanjilal", "Steve Smith", "Anand John" };
[HttpGet]
public IEnumerable<string> Get()
{
return authors;
}
}
When you make a Get request from an HTTP client such as Postman, here’s how the versions will be reported.
You can report the deprecated versions as well. To do this, you should pass an extra parameter to the ApiVersion method as shown in the code snippet given below.
[ApiVersion("1.0", Deprecated = true)]
Map to a specific version of an action method
There's another important attribute named MapToApiVersion. You can use it to map to a specific version of an action method. The following code snippet shows how this can be accomplished.
[HttpGet("{id}")]
[MapToApiVersion("2.0")]
public string Get(int id)
{
return authors[id];
}
Complete API versioning example in ASP.NET Core
Here is the complete source code of the DefaultController for your reference.
[Route("api/[controller]")]
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[ApiVersion("2.0")]
public class DefaultController : ControllerBase
{
string[] authors = new string[]
{ "Joydip Kanjilal", "Steve Smith", "Stephen Jones" };
[HttpGet]
public IEnumerable<string> Get()
{
return authors;
}
[HttpGet("{id}")]
[MapToApiVersion("2.0")]
public string Get(int id)
{
return authors[id];
}
}
API versioning strategies in ASP.NET Core
There are several ways in which you can version your API in ASP.NET Core. In this section we’ll explore each of them.
Pass version information as QueryString parameters
In this case, you would typically be passing the version information as part of the query string as shown in the URL given below.
http://localhost:25718/api/default?api-version=1.0
Pass version information in the HTTP headers
If you are to pass version information in the HTTP headers, you should set it up in the ConfigureServices method as shown in the code snippet given below.
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
config.ApiVersionReader = new HeaderApiVersionReader("api-version");
});
Once this has been set up, you can invoke an action method pertaining to a specific version of the API as shown in Figure 3.
Pass version information in the URL
Yet another method of passing version information is passing version information as part of the route. This is the simplest way of versioning your API but there are certain caveats. First off, if you use this strategy then your clients will need to update the URL whenever a new version of the API is released. Consequently, this approach breaks a fundamental principle of REST that states that the URL of a particular resource should never change.
To implement this versioning strategy, you should specify the route information in your controller as shown below.
[Route("api/v{version:apiVersion}/[controller]")]
The following code listing shows how you can set this up in your controller class.
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
public class DefaultController : ControllerBase
{
string[] authors = new string[]
{ "Joydip Kanjilal", "Steve Smith", "Stephen Jones" };
[HttpGet]
public IEnumerable<string> Get()
{
return authors;
}
[HttpGet("{id}")]
[MapToApiVersion("2.0")]
public string Get(int id)
{
return authors[id];
}
}
Here is how you can call the default HTTP to get the method of the DefaultController class.
http://localhost:25718/api/v1.0/default
To invoke the other HTTP GET method, i.e., the one that accepts a parameter, specify the following either in the web browser or an HTTP client such as Postman.
http://localhost:25718/api/v2.0/default/1
Deprecate one or more versions of your API
Assume you have multiple versions of your API but you would like to deprecate one or more of them. You can do this easily — you just need to specify the Deprecated property of the ApiVersionAttribute class to true as shown in the code snippet given below.
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("1.1", Deprecated = true)]
[ApiVersion("2.0")]
public class DefaultController : ControllerBase
{
//Usual code
}
API versioning in ASP.NET Core is now seamless thanks to the introduction of the Microsoft.AspNetCore.Mvc.Versioning package. There are several ways to version your API — you just need to decide the best strategy based on your requirements. You can also use multiple versioning schemes for your API. This adds a lot of flexibility since the clients can choose any of the supported versioning schemes.
How to do more in ASP.NET Core:
- How to use Data Transfer Objects in ASP.NET Core 3.1
- How to handle 404 errors in ASP.NET Core MVC
- How to use dependency injection in action filters in ASP.NET Core 3.1
- How to use the options pattern in ASP.NET Core
- How to use endpoint routing in ASP.NET Core 3.0 MVC
- How to export data to Excel in ASP.NET Core 3.0
- How to use LoggerMessage in ASP.NET Core 3.0
- How to send emails in ASP.NET Core
- How to log data to SQL Server in ASP.NET Core
- How to schedule jobs using Quartz.NET in ASP.NET Core
- How to return data from ASP.NET Core Web API
- How to format response data in ASP.NET Core
- How to consume an ASP.NET Core Web API using RestSharp
- How to perform async operations using Dapper
- How to use feature flags in ASP.NET Core
- How to use the FromServices attribute in ASP.NET Core
- How to work with cookies in ASP.NET Core
- How to work with static files in ASP.NET Core
- How to use URL Rewriting Middleware in ASP.NET Core
- How to implement rate limiting in ASP.NET Core
- How to use Azure Application Insights in ASP.NET Core
- Using advanced NLog features in ASP.NET Core
- How to handle errors in ASP.NET Web API
- How to implement global exception handling in ASP.NET Core MVC
- How to handle null values in ASP.NET Core MVC
- Advanced versioning in ASP.NET Core Web API
- How to work with worker services in ASP.NET Core
- How to use the Data Protection API in ASP.NET Core
- How to use conditional middleware in ASP.NET Core
- How to work with session state in ASP.NET Core
- How to write efficient controllers in ASP.NET Core