Api Version with Different Names

778 Views Asked by At

I am using asp dotnet core 3.1 for my project. Currently, I am versioning my actions by 1.0 or 2.1 as its the suggested way to do it. But I want to version my actions by, for example, schooljob or some other random string value. I researched a lot, some websites claims it's possible to do 1_0instead 1.0 but didn't say how to. Also, I don't know is it possible to change versioning to schooljob.

What I really want to do is for my URLs is changing them from ...someUrl/1.0/getTeachers to ...someUrl/schooljob/getTeachers so I can finally group them better in Swagger.

// SchoolController.cs
[ApiVersion("1.0")]  //I want to change it as [ApiVersion("schooljob")]
[ApiController]
[Route("{version:apiVersion}")]
public class SchoolController : AbstractController
{
   ...

   [HttpPost("schooljob/getstudents")]
   public JsonResult GetStudents([FromBody]Student student)
   { ... }
}
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    c.SwaggerDoc("schooljob", new OpenApiInfo
    {
        Version = "schooljob",
        Title = "School Job"
    });
    // c.AddSecurityDefinition... and other swagger configurations
}   
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
    app.UseSwagger(c =>
    {
        c.RouteTemplate = "mainproject/swagger/{documentname}/swagger.json";
    });
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/mainproject/swagger/schooljob/swagger.json", "School Job");
        c.RoutePrefix = "mainproject/swagger";
    });
}
1

There are 1 best solutions below

2
On

Short answer - no - you cannot define an API version this way. It has a prescribed format which cannot be changed. This is important for collation, sorting, and comparison. For example, 1.0 > 1.0-beta.

It looks like what you want to do is group your APIs. The default behavior is to use the formatted API version text as the group name of API descriptions. This is the bridge to other libraries such as Swashbuckle or NSwag for grouping. There are several ways you can change this behavior. You can change it as an API Explorer extension (a la IApiDescriptionProvider) or a OpenAPI/Swagger document generator extension. To get the results you want, you just need to reorder/group the results.

Most grouping is done by API version, but it is certainly possible to group by a resource or category. Since API Versioning overrides the default group name, you'll need to re-extract the original group name defined in the source attribute or use a custom attribute. You might be able to build it up from template convention or other metadata.

Example

First, you need a way to compare 2 ApiDescription instances the way you want:

public class MyComparer : IComparer<ApiDescription>
{
    public int Compare(ApiDescription x, ApiDescription y)
    {
        if (x == null)
        {
            return y == null ? 0 : -1;
        }

        if (y == null)
        {
            return 1;
        }

        var comparer = StringComparer.OrdinalIgnoreCase;
        var result = comparer.Compare(x.GroupName, y.GroupName);

        // compare by group name, then api version
        return result == 0 ? x.GetApiVersion().CompareTo(y.GetApiVersion()) : result;
    }
}

Then, you can create a custom IApiDescriptionProvider which processes the discovered descriptions:

public class MyApiDescriptionProvider : IApiDescriptionProvider
{
    // important: you may need to increase the value to run later in the sequence
    public int Order => 0;

    public void OnProvidersExecuting(ApiDescriptionProviderContext context) { }

    public void OnProvidersExecuted(ApiDescriptionProviderContext context)
    {
        var sorted = context.Results.ToList();

        sorted.Sort(new MyComparer());
        context.Results.Clear();

        for (var i = 0; i < sorted.Count; i++)
        {
            context.Results.Add(sorted[i]);
        }
    }
}

Finally, register your provider in your setup:

services.TryAddEnumerable(
  ServiceDescriptor.Transient<IApiDescriptionProvider, MyApiDescriptionProvider>() );