Back to: ASP.NET Core Web API Tutorials
Media Type Versioning in ASP.NET Core Web API
In this article, I will discuss how to Implement Web API Versioning using Media Type in ASP.NET Core Web API Applications with Examples. Please read our previous article discussing ASP.NET Core Web API Versioning using Custom HTTP Header.
What is Media Type Versioning in Web API?
Media Type Versioning, also known as “Content Negotiation”, or MIME type versioning, or “Accept Header Versioning,” is a technique used in Web APIs to manage multiple versions of the API through the HTTP Accept header.
This method relies on specifying the version of the API within the media type requested by the client. This approach uses the HTTP Accept header, allowing the client to indicate which version of the API it expects to communicate with by including a version parameter in the media type.
How Media Type Versioning Works in ASP.NET Core Web API?
Media Type Versioning works by using custom media types in the HTTP headers to specify the version of the API that the client wants to interact with. Let’s have a look at how it is implemented in ASP.NET Core Web API:
- Client Request: The client sends an HTTP request with the Accept header set to a custom media type that includes the version information. For example, a client might send a request with an Accept header of application/json;v=1.0 to indicate that it wants to use version 1.0 of the API and expects a JSON response.
- Server Handling: The server inspects the Accept header and determines which version of the API should handle the request based on the media type specified.
- Routing to the Correct Version: The server routes the request to the appropriate version of the API controller or action method that corresponds to the specified media type.
- Response: The server processes the request and returns a response in the format specified by the media type.
When the ASP.NET Core pipeline receives a request, it checks the Accept header to extract the version information. Based on the Version information, the API Versioning middleware routes the request to the appropriate version of the controller action.
How Do We Implement Media Type Versioning in ASP.NET Core Web API?
Implementing Media Type versioning in an ASP.NET Core Web API Application involves several steps. These include Installing the Required NuGet Packages, Configuring Media Type Versioning Configuration in Program.cs class file, modifying controllers to support multiple versions, and ensuring that the clients specify the version number using the standard Accept Header in each request.
Let us proceed with the step-by-step implementation of the Media Type Versioning in the ASP.NET Core Web API Application. Create a new ASP.NET Core Web API Project named MediaTypeAPIVersioning.
Install Necessary Packages
To implement Media Type Versioning or any other versioning in ASP.NET Core Web API, we need to install Microsoft.AspNetCore.Mvc.Versioning package. You can install this Package through the NuGet Package Manager for Solution or via the Package Manager Console as follows:
Install-Package Microsoft.AspNetCore.Mvc.Versioning
Configure Media Type Versioning in the Program.cs File
In the Program.cs file, we need to configure the API versioning service. We also need to specify that the API version will be read from the request Media Type Header. So, modify the Program.cs class file as follows. The following code is self-explained, so please read the comment lines for a better understanding.
using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Reflection; namespace MediaTypeAPIVersioning { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); // Add API Versioning builder.Services.AddApiVersioning(options => { // Specify the default API version // Default version: 1.0 options.DefaultApiVersion = new ApiVersion(1, 0); // If the client does not specify a version, use the default version options.AssumeDefaultVersionWhenUnspecified = true; // Optional: to include API versions in the response headers options.ReportApiVersions = true; // Read the version number from the Media Type Header options.ApiVersionReader = new MediaTypeApiVersionReader(); }); // Add Swagger generation builder.Services.AddSwaggerGen(options => { // Defines two Swagger documents, one for API version 1.0 and another for version 2.0. // Define a Swagger document for API version 1.0 options.SwaggerDoc("1.0", new OpenApiInfo { Title = "My API", Version = "1.0" }); // Define a Swagger document for API version 2.0 options.SwaggerDoc("2.0", new OpenApiInfo { Title = "My API", Version = "2.0" }); // Resolving Conflicts options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); // Doc Inclusion Predicate options.DocInclusionPredicate((version, apiDesc) => { // Attempt to get the MethodInfo for the API Endpoint if (!apiDesc.TryGetMethodInfo(out MethodInfo method)) return false; //If the MethodInfo for the API description cannot be retrieved, the endpoint is excluded. // Extracts the versions specified by [ApiVersion] attributes at the method level. var methodVersions = method.GetCustomAttributes(true) .OfType<ApiVersionAttribute>() .SelectMany(attr => attr.Versions); // Extracts the versions specified by [ApiVersion] attributes at the controller level. var controllerVersions = method.DeclaringType? .GetCustomAttributes(true) .OfType<ApiVersionAttribute>() .SelectMany(attr => attr.Versions); // Combining Versions var allVersions = methodVersions.Union(controllerVersions).Distinct(); // Checks if any of the combined versions match the version specified in the Swagger document return allVersions.Any(v => v.ToString() == version); }); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { // Enable middleware to serve generated Swagger as a JSON endpoint app.UseSwagger(); // Enable middleware to serve Swagger UI app.UseSwaggerUI(options => { // Define the Swagger endpoints for each version options.SwaggerEndpoint("/swagger/1.0/swagger.json", "My API V1"); options.SwaggerEndpoint("/swagger/2.0/swagger.json", "My API V2"); }); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
Define the Product Model
To demonstrate how to handle different versions of an API using Media Type Versioning, let’s define a product model and then use some hardcoded data to return from the action methods. So, create a class file named Product.cs within the Models folder and then copy and paste the following code.
namespace MediaTypeAPIVersioning.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public double Price { get; set; } public string Category { get; set; } } }
Define API Versions in Controllers
Decorate controller actions with ApiVersion attributes to specify which API versions they support. So, create a new Empty API Controller named ProductsController within the Controllers folder and copy and paste the following code. As you can see in the below code, using the [ApiVersion] attribute, we specify the version number the action method supports.
using MediaTypeAPIVersioning.Models; using Microsoft.AspNetCore.Mvc; namespace MediaTypeAPIVersioning.Controllers { [Route("api/[controller]")] [ApiController] public class ProductsController : ControllerBase { [HttpGet] [ApiVersion("1.0")] public IEnumerable<Product> GetV1() { // Hardcoded list of products for version 1.0 return new List<Product> { new Product { Id = 1, Name = "Apple", Price = 1.50, Category = "Fruit" }, new Product { Id = 2, Name = "Bread", Price = 2.25, Category = "Bakery" } }; } [HttpGet] [ApiVersion("1.0")] [Route("{Id}")] public Product GetByIdV1(int Id) { return new Product { Id = Id, Name = "Apple", Price = 1.50, Category = "Fruit" }; } [HttpGet] [ApiVersion("2.0")] public IEnumerable<Product> GetV2() { // Hardcoded list of products for version 2.0, including additional fields return new List<Product> { new Product { Id = 1, Name = "Apple", Price = 1.50, Category = "Fruit" }, new Product { Id = 2, Name = "Bread", Price = 2.25, Category = "Bakery" }, new Product { Id = 3, Name = "Carrot", Price = 0.75, Category = "Vegetable" } }; } } }
Client Request with Accept Media Type Header
To access different versions of the API, clients should include the version number in the Accept headers of their HTTP requests. For example, to request version 1.0 of the API, the client would set the Accept HTTP header value as application/json;v=1.0. Similarly, for version 2.0, the client would set the Accept HTTP header value as application/json;v=2.0.
Testing API Version 1.0:
Testing API Version 2.0:
Applying ApiVersion Attribute at the Controller Level:
We can also apply the ApiVersion Attribute at the Controller level. Let us understand this with an example. Let us create two Controllers. So, for simplicity, let us modify the Products Controller as follows. Here, you can see we have two Controllers, and each controller is decorated with the ApiVersion Attribute with a different version number.
using MediaTypeAPIVersioning.Models; using Microsoft.AspNetCore.Mvc; namespace MediaTypeAPIVersioning.Controllers { [ApiController] [Route("api/products")] [ApiVersion("1.0")] public class ProductsV1Controller : ControllerBase { [HttpGet] public IEnumerable<Product> GetProductsV1() { // Returns a hardcoded list of products specifically for version 1.0 of the API. return new List<Product> { new Product { Id = 1, Name = "Apple", Price = 1.50, Category = "Fruit" }, new Product { Id = 2, Name = "Bread", Price = 2.25, Category = "Bakery" } }; } [HttpGet] [Route("{Id}")] public Product GetProductsByIdV1(int Id) { // Returns a hardcoded product matching the provided ID. return new Product { Id = Id, Name = "Apple", Price = 1.50, Category = "Fruit" }; // Returns a Product object. } } [ApiController] [Route("api/products")] [ApiVersion("2.0")] public class ProductsV2Controller : ControllerBase { [HttpGet] public IEnumerable<Product> GetProductsV2() { // Returns a hardcoded list of products specifically for version 2.0 of the API, with an additional product. return new List<Product> { new Product { Id = 1, Name = "Apple", Price = 1.50, Category = "Fruit" }, new Product { Id = 2, Name = "Bread", Price = 2.25, Category = "Bakery" }, new Product { Id = 3, Name = "Carrot", Price = 0.75, Category = "Vegetable" } }; } } }
In the next article, I will discuss Real-Time Hotel Booking Application Development using ASP.NET Core Web API. In this article, I explain How to Implement ASP.NET Core Web API Versioning using Media type Header with Examples. I hope you enjoy this ASP.NET Core Web API Versioning using Media Type article.