Home > Back-end >  How to get the route group prefix when using RouteGroupBuilder in ASP.NET Core 7 WebApi
How to get the route group prefix when using RouteGroupBuilder in ASP.NET Core 7 WebApi

Time:11-12

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 or GetUriByRouteValues with a routeName 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");
  • Related