Elegant API Versioning In ASP.NET Core - Part Two

In this article, I’m going to explain URL based API versioning. I have explained the query string-based versioning in one of my articles- Elegant API Versioning In ASP.NET Core

For the implementation, I will be using,

  • Microsoft Visual Studio Community 2019
  • Version 16.4.5
  • ASP.NET Core 3.1
  • Postman for testing the API

I’m going to use the same models, controllers, and actions which have been used in my previous article.

Here we are going to create APIs as below

  • http://localhost:12345/api/v1/product/{id} -
  • http://locahost:1234/api/v2/product/{id}

Note: port number will be varied depending on our workstation.

Assume that we two respond models as below

public class ProductResponseV1 {
    public Guid Id {
        get;
        set;
    }
    public string Name {
        get;
        set;
    }
}

This model will be used as a response when invoking the API with version v1

public class ProductResponseV2 {
    public Guid Id {
        get;
        set;
    }
    public string ProductName {
        get;
        set;
    }
}

This model will be used as a response while invoking the API with version v2

In order to implement the versioning, as I explained in my previous article, we need to install a NuGet package Microsoft.AspNetCore.Mvc.Versioning

Then we need to configure the versioning in Startup.cs class as mentioned below

In public void ConfigureServices(IServiceCollection services) method, we need to make the below changes. This method gets called by the runtime. Use this method to add services to the container.

public void ConfigureServices(IServiceCollection services) {
    services.AddControllers();
    services.AddApiVersioning(apiv => {
        apiv.AssumeDefaultVersionWhenUnspecified = true;
        apiv.DefaultApiVersion = ApiVersion.Default;
    });
}

In public void Configure(IApplicationBuilder app, IWebHostEnvironment env) method will look like below. This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
    }
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints => {
        endpoints.MapControllers();
    });
}

Now let us go to the Controller class and see what all changes needs to be made to implement the URL based versioning.

Let us go ahead add two action methods as below

[HttpGet("{productId}")]
public IActionResult GetProductV1([FromRoute] Guid productId) {
    var product = new ProductResponseV1 {
        Id = productId,
            Name = "Sanitizer"
    };
    return Ok(product);
}

This method will return the respond model ProductResponseV1

[HttpGet("{productId}")]
public IActionResult GetProductV2([FromRoute] Guid productId) {
    var product = new ProductResponseV2 {
        Id = productId,
            ProductName = "Sanitizer"
    };
    return Ok(product);
}

This model will return the response model ProductResponseV2

If we are going to run the program, we will be getting the below exception

An unhandled exception occurred while processing the request.

AmbiguousMatchException: The request matched multiple endpoints. Matches:

ElegantAPIVersion.Controllers.ProductController.GetProductV2 (ElegantAPIVersion)
ElegantAPIVersion.Controllers.ProductController.GetProductV1 (ElegantAPIVersion)

Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)

To resolve this issue, we need to apply the below changes to the controller class.

[ApiController]
[Route("api/v{version:ApiVersion}/product")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
publicclassProductController: ControllerBase {
    [HttpGet("{productId}")]
    public IActionResult GetProductV1([FromRoute] Guid productId) {
            var product = new ProductResponseV1 {
                Id = productId,
                    Name = "Sanitizer"
            };
            return Ok(product);
        }
        [HttpGet("{productId}")]
        [ApiVersion("2.0")]
    public IActionResult GetProductV2([FromRoute] Guid productId) {
        var product = new ProductResponseV2 {
            Id = productId,
                ProductName = "Sanitizer"
        };
        return Ok(product);
    }
}

We are good to go and validate the APIs. Let us use Postman to validate the APIs

Execute the below API and review the response.

http://localhost:123456/api/v1/product/00000000-0000-0000-0000-000000000000

Result

{
    "id": "00000000-0000-0000-0000-000000000000",
    "name": "Sanitizer"
}

This is nothing but the ProductResponseV1

Now let us run the v2 API

http://localhost:123456/api/v2/product/00000000-0000-0000-0000-000000000000

Result

{
    "id": "00000000-0000-0000-0000-000000000000",
    "productName": "Sanitizer"
}

This is returning the ProductResponseV2 model

I have covered the URL based versioning in .NET Core in this article. I hope this will be helpful, if you are going to introduce any versioning to your APIs. Thank you so much for reading my article. Please provide your feedback in the comment box below.