Skip to content

Instantly share code, notes, and snippets.

@dj-nitehawk
Last active April 3, 2025 03:37
Show Gist options
  • Save dj-nitehawk/fbefbcb6273d372e5e5913accb18ab76 to your computer and use it in GitHub Desktop.
Save dj-nitehawk/fbefbcb6273d372e5e5913accb18ab76 to your computer and use it in GitHub Desktop.
Asp.Versioning.Http Example
using Asp.Versioning;
using Asp.Versioning.Conventions;
using FastEndpoints;
using FastEndpoints.AspVersioning;
using FastEndpoints.Swagger;
VersionSets.CreateApi(">>Orders<<", v => v
.HasApiVersion(1.0)
.HasApiVersion(2.0));
VersionSets.CreateApi(">>Inventory<<", v =>
{
v.HasApiVersion(1.0);
v.HasApiVersion(1.1);
});
var bld = WebApplication.CreateBuilder();
bld.Services
.AddFastEndpoints()
.AddVersioning(o =>
{
o.DefaultApiVersion = new(1.0);
o.AssumeDefaultVersionWhenUnspecified = true;
o.ApiVersionReader = new HeaderApiVersionReader("X-Api-Version");
})
.SwaggerDocument(o =>
{
o.DocumentSettings = x =>
{
x.DocumentName = "version one";
x.ApiVersion(new(1.0));
};
o.AutoTagPathSegmentIndex = 0;
})
.SwaggerDocument(o =>
{
o.DocumentSettings = x =>
{
x.DocumentName = "version one point one";
x.ApiVersion(new(1.1));
};
o.AutoTagPathSegmentIndex = 0;
})
.SwaggerDocument(o =>
{
o.DocumentSettings = x =>
{
x.DocumentName = "version two";
x.ApiVersion(new(2.0));
};
o.AutoTagPathSegmentIndex = 0;
});
var app = bld.Build();
app.UseFastEndpoints()
.UseSwaggerGen();
app.Run();
public class GetInvoices_v1 : EndpointWithoutRequest
{
public override void Configure()
{
Get("invoices");
AllowAnonymous();
Options(x => x
.WithVersionSet(">>Orders<<")
.MapToApiVersion(1.0));
}
public override async Task HandleAsync(CancellationToken c)
{
await SendAsync("v1 - orders");
}
}
public class GetInvoices_v2 : EndpointWithoutRequest
{
public override void Configure()
{
Get("invoices");
AllowAnonymous();
Options(x => x
.WithVersionSet(">>Orders<<")
.MapToApiVersion(2.0));
}
public override async Task HandleAsync(CancellationToken c)
{
await SendAsync("v2 - orders");
}
}
public class GetStockItems_v1 : EndpointWithoutRequest
{
public override void Configure()
{
Get("stock-items");
AllowAnonymous();
Options(x => x
.WithVersionSet(">>Inventory<<")
.MapToApiVersion(1.0));
}
public override async Task HandleAsync(CancellationToken c)
{
await SendAsync("v1 - inventory");
}
}
public class GetStockItems_v1_1 : EndpointWithoutRequest
{
public override void Configure()
{
Get("stock-items");
AllowAnonymous();
Options(x => x
.WithVersionSet(">>Inventory<<")
.MapToApiVersion(1.1));
}
public override async Task HandleAsync(CancellationToken c)
{
await SendAsync("v1.1 - inventory");
}
}
@VictorioBerra
Copy link

I think HasApiVersion(...) should be HasApiVersion(new(...))

@dj-nitehawk
Copy link
Author

@VictorioBerra it's an extension method overload coming from using Asp.Versioning.Conventions;

@Sabari2810
Copy link

Sabari2810 commented Nov 14, 2024

I am using UrlUrlSegmentApiVersionReader(), the swagger documentation is not reflecting the version in the endpoint
Swagger-Reference

@byte2pixel
Copy link

byte2pixel commented Apr 2, 2025

Say you are building a HATEOAS API.

I was using ResponseMapper but building the "Links" to append to the response. I am unsure of the best way to get the API Version to append to the end of the link.

links.Add(
    new Link
    {
        Rel = "cancel",
        Href = $"/api/jobs/{trackingId}/cancel",
        Method = "POST",
    }
);

For example, I need to add v1 or v# to the end. Maybe I should have the ReponseMapper do the basic mapping and add the links later in the actual endpoint instead of using SendMappedAsync then I have access to the Definition.Version.Current. Is that the best solution or am I not seeing something?

Or maybe the answer is don't build a HATEOAS API 🤣

@dj-nitehawk
Copy link
Author

@byte2pixel
i wouldn't recommend HATEOAS to my worst enemy 😜 (there's a reason why it's not the defacto in the industry)
if you just need route based versioning, stick to one of the built-in versioning strategies.
appending of the version number is the default there:

image

@byte2pixel
Copy link

Haha ok.
I am not set on it. I opted out of using route-based versioning anyway. The approach above with FastEndpoints.AspVersioning; seems much better.

I also found that LinkGenerator seems to work very well. The only problem I would run into is that I need to probably pass in what version they are using to know what path name to use when calling the method on LinkGenerator.

The more I dig into this the less I am inclined to have these links. The thought seems nice if there is no cancel link the UI doesn't have to show the cancel button, if there is then it can show it and use the href to call if it is pressed.

It is one of those, it seems like it would be nice, but also be very difficult to do it right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment