The .NET v7 framework introduced the concept of Route Groups to group mapped endpoints together based on a common prefix in the path:
app.MapGroup("/public/todos").MapTodosApi();
Now, in order to return an absolute path for the Created
response (i.e. to a POST
request), I need that route path prefix inside the service. This usecase is even discussed in the official documentation:
Instead of using relative addresses for the Location header in the 201 Created result, it’s also possible to use GroupRouteBuilder.GroupPrefix to construct a root-relative address
However, the GroupRouteBuilder
class does not exist. There is only a RouteGroupBuilder
, but an instance of it has no member GroupPrefix
. Any suggestions how to actually get the prefix?
CodePudding user response:
Looks like the GroupPrefix
property was removed from RouteGroupBuilder
(after name was changed). Here https://github.com/dotnet/aspnetcore/issues/41427 is a comment that says:
// It was too easy for this to get out of sync with RouteGroupContext.Prefix in real life given RouteGroupBuilder decorators.
To get the Prefix, you can bring in HttpContextAccesor:
builder.Services.AddHttpContextAccessor();
Then call GetEndpoint()
on the HttpContext
, convert result to RouteEndpoint
, and look at the RoutePattern
app.MapGroup("/public/todos").MapTodosApi();
app.Run();
public class Todo
{
public int Id { get; set; }
public string? Title { get; set; }
public bool IsComplete { get; set; }
}
public static class TodosApi
{
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
group.MapPost("/", CreateTodo);
return group;
}
public static async Task<Created<Todo>> CreateTodo(Todo todo, IHttpContextAccessor httpContext)
{
var endpoint = httpContext.HttpContext.GetEndpoint();
var pattern = ((RouteEndpoint)endpoint).RoutePattern;
return TypedResults.Created($"{todo.Id}", todo);
}
}
CodePudding user response:
That document you linked - although being a preview one - mentions:
use
GetPathByRouteValues
orGetUriByRouteValues
with arouteName
for even more options.
The offical one about link generation is here.
It shows how to inject and use a LinkGenerator (which also contains above 2 mentioned methods) in combination with such a named route.
Endpoints can be given names in order to generate URLs to the endpoint. Using a named endpoint avoids having to hard code paths in an app
To apply this to the example you are referring to;
give a name to the route that retrieves a Todo
item,
and use that one when generating the created-at url.
var group = app.MapGroup("todos");
group
.MapGet("/{id}", (int id) =>
{
// ...
return new Todo { Id = id };
})
.WithName("GetToDo");
group
.MapPost("/", (Todo todo, LinkGenerator linkGenerator) =>
{
// ...
var url = linkGenerator.GetPathByName("GetToDo", new { id = todo.Id });
return TypedResults.Created(url, todo);
});
The idea about using the group prefix when composing the created-at url will not suffice in case the pattern of the GET (get-item) endpoint has an extra segment, since that one will not be included. You'll end up with combining multiple hard coded string parts.
In case the get-todo-item endpoint from above example would have an additional extra
segment - as shown in the example below - then the created-at url for ToDo
item with id 123
must be
/todos/extra/123
Combining linkGenerator
with a named route takes care of that.
group
.MapGet("/extra/{id}", (int id) =>
{
// ...
return new Todo { Id = id };
})
.WithName("GetToDo");