Routing
WARNING
The route argument to method name matching is case-sensitive.
Wolverine HTTP endpoints need to be decorated with one of the [WolverineVerb("route")] attributes that expresses the routing argument path in standard ASP.Net Core syntax (i.e., the same as when using MVC Core or Minimal API).
If a parameter argument to the HTTP handler method exactly matches a route argument, Wolverine will treat that as a route argument and pass the route argument value at runtime from ASP.Net Core to your handler method. To make that concrete, consider this simple case from the test suite:
[WolverineGet("/name/{name}")]
public static string SimpleStringRouteArgument(string name)
{
return $"Name is {name}";
}In the sample above, the name argument will be the value of the route argument at runtime. Here's another example, but this time using a numeric value:
[WolverineGet("/age/{age}")]
public static string IntRouteArgument(int age)
{
return $"Age is {age}";
}The following code snippet from WolverineFx.Http itself shows the native .NET valid route parameter types that are supported at this time:
public static readonly Dictionary<Type, string> TypeOutputs = new()
{
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(sbyte), "sbyte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(float), "float" },
{ typeof(short), "short" },
{ typeof(int), "int" },
{ typeof(double), "double" },
{ typeof(long), "long" },
{ typeof(ushort), "ushort" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
{ typeof(Guid), typeof(Guid).FullName! },
{ typeof(DateTime), typeof(DateTime).FullName! },
{ typeof(DateTimeOffset), typeof(DateTimeOffset).FullName! },
{ typeof(DateOnly), typeof(DateOnly).FullName! }
};WARNING
Wolverine will return a 404 status code if a route parameter cannot be correctly parsed. So passing "ABC" into what is expected to be an integer will result in a 404 response.
Strong Typed Identifiers 5.0
Wolverine.HTTP can support any type as a route argument that implements a TryParse() method. At this point, both the Vogen and StronglyTypedId tools do this for you, and value types generated by these tools are legal route argument variables for Wolverine.HTTP now.
As an example from the Wolverine tests, let's say you have an identity type like this sample that uses StronglyTypedId:
[StronglyTypedId(Template.Guid)]
public readonly partial struct LetterId;You can use the LetterId type as a route argument as this example shows:
[WolverineGet("/sti/aggregate/longhand/{id}")]
public static async ValueTask<StrongLetterAggregate> Handle2(LetterId id, IDocumentSession session) =>
(await session.Events.FetchLatest<StrongLetterAggregate>(id.Value))!;
// This is an equivalent to the endpoint above
[WolverineGet("/sti/aggregate/{id}")]
public static StrongLetterAggregate Handle(
[ReadAggregate] StrongLetterAggregate aggregate) => aggregate;Route Prefixes 5.14
Wolverine.HTTP supports route prefix groups to automatically prepend a common prefix to your endpoint routes. This is useful for API versioning, organizing endpoints under a common base path, or grouping related endpoints.
Global Route Prefix
Use the RoutePrefix method on WolverineHttpOptions to set a prefix that applies to all Wolverine HTTP endpoints:
app.MapWolverineEndpoints(opts =>
{
// All endpoints will be prefixed with /api
// e.g., /orders becomes /api/orders
opts.RoutePrefix("api");
});Namespace-Specific Prefix
You can target endpoints by their handler class namespace:
app.MapWolverineEndpoints(opts =>
{
// Only endpoints in this namespace (and child namespaces)
// will get the prefix
opts.RoutePrefix("api/orders",
forEndpointsInNamespace: "MyApp.Features.Orders");
opts.RoutePrefix("api/customers",
forEndpointsInNamespace: "MyApp.Features.Customers");
});When multiple namespace prefixes could match (e.g., MyApp and MyApp.Features), the most specific (longest) matching namespace wins.
[RoutePrefix] Attribute
You can also apply a prefix to all endpoints in a specific class using the [RoutePrefix] attribute:
[RoutePrefix("api/v2")]
public class OrderEndpoints
{
// Route becomes /api/v2/orders
[WolverineGet("/orders")]
public string GetOrders() => "orders";
// Route becomes /api/v2/orders/{id}
[WolverineGet("/orders/{id}")]
public string GetOrder(int id) => $"order {id}";
}Precedence
When multiple prefix sources are configured, the following precedence applies (most specific wins):
[RoutePrefix]attribute on the endpoint class- Namespace-specific prefix (most specific namespace match)
- Global prefix
Only one prefix is applied per endpoint -- they do not stack.
Route Prefixes
You can apply a common URL prefix to all endpoints in a handler class using the [RoutePrefix] attribute:
[RoutePrefix("/api/v1")]
public static class V1Endpoints
{
// Resolves to /api/v1/orders
[WolverineGet("/orders")]
public static string GetOrders() => "V1 Orders";
// Resolves to /api/v1/orders/{id}
[WolverineGet("/orders/{id}")]
public static string GetOrder(int id) => $"V1 Order {id}";
}You can also configure route prefixes globally or by namespace in your application setup:
app.MapWolverineEndpoints(opts =>
{
// Apply a global prefix to all Wolverine endpoints
opts.RoutePrefix("api");
// Apply a prefix to all endpoints in a specific namespace
opts.RoutePrefix("api/v2", forEndpointsInNamespace: "MyApp.Endpoints.V2");
});When multiple prefix sources apply, the precedence is: [RoutePrefix] attribute > namespace prefix > global prefix. Only one prefix is applied per endpoint — they do not stack.
API Versioning
For API versioning scenarios, Wolverine recommends using route prefixes to version your endpoints. This approach is simple, explicit, and requires no additional packages:
[RoutePrefix("/api/v1")]
public static class OrdersV1
{
[WolverineGet("/orders")]
public static OrderListResponse GetOrders() => /* ... */;
}
[RoutePrefix("/api/v2")]
public static class OrdersV2
{
// V2 returns a different shape
[WolverineGet("/orders")]
public static OrderListResponseV2 GetOrders() => /* ... */;
}You can also version by namespace:
app.MapWolverineEndpoints(opts =>
{
opts.RoutePrefix("api/v1", forEndpointsInNamespace: "MyApp.Endpoints.V1");
opts.RoutePrefix("api/v2", forEndpointsInNamespace: "MyApp.Endpoints.V2");
});This lets you organize versioned endpoints into separate namespaces and apply the version prefix automatically without any attributes on individual classes.
Route Name
You can add a name to the ASP.Net route with this property that is on all of the route definition attributes:
[WolverinePost("/named/route", RouteName = "NamedRoute")]
public string Post()
{
return "Hello";
}
