Search code examples
c#asp.net-core.net-coreasp.net-core-webapirefit

Refit: versioned routes


We were using Refit on one of your API's to create and share a Client package for that API.

ICategoryApi.cs

[Post("/category")]
Task CreateCategoryAsync([Body] CategoryCommandDto createCategoryCommandDto);

and everything was working fine with a controller like

CategoryController.cs

[ApiController]
[Route("[controller]")]
public class CategoriesController : ControllerBase
{
    [HttpPost]
    [ProducesResponseType((int)HttpStatusCode.Created)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> CreateCategory([FromBody] CategoryCommandDto createCategoryCommandDto)
    {
          //some code
    }
}

The problem is that now we've added api versioning, and we choosed to version by route.

So now the endpoint /category looks like /v1/category and we will create a /v2/category soon.

Is there a way to configure refit (through attributes or similar) for it to understand my versioned routes?

I want to avoid having to write a new client for every new version of the API and including the version in the endpoint route like

ICategoryApiV1.cs

[Post("/v1/category")]
Task CreateCategoryAsync([Body] CategoryCommandDto createCategoryCommandDto);

Imagine that the client is bigger and has a lot of methods, not just one. Also not all the methods may change between versions.


Solution

  • You can achieve this in a different way: 1) Use like an argument from method;

    ICategoryApiV1.cs
    
    [Post("/{v}/category")]
    Task CreateCategoryAsync([Body] CategoryCommandDto createCategoryCommandDto, [AliasAs("v")]int version = 1);
    

    2) Define a property inside of CategoryCommandDto;

    public class CategoryCommandDto
    {
        // Properties can be read-only and [AliasAs] isn't required
        public int v { get { return 1; } }
          .....
    }
    

    3) Define a baseUrl for httpClient during ICategoryApi creation

    services.AddRefitClient<ICategoryApi>()
        .ConfigureHttpClient(c => c.BaseAddress = new Uri($"https://api.example.com/{version}"));
    

    4) Or if you need some advanced calculation you can add a custom HttpHandler and configure inside yours client.

    services.AddRefitClient<ICategoryApi>(settings)
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));
            .AddHttpMessageHandler<VersionHandler>()