-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Improve minimal api routing with grouping #36007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Very express like, I like it, but we're pretty much done with new features for .NET 6, we'll consider it for .NET 7. |
I'm not sure we need to implement cc @khalidabuhakmeh since you implemented something similar. |
Yes. |
@ziaulhasanhamim right, we have something similar today with IApplicationBuilder.Map, but you don't get an IEndpointRouteBuilder. I could envision something like this: app.MapGroup("/api/users", group =>
{
group.UseCors(); // Does this make sense?
group.MapGet("/", (UsersDbContext db) => db.ToListAsync());
group.MapGet("/{id}", (int id, UsersDbContext db) => db.Users.Find(id));
}); |
@davidfowl yes right you are. That doesn't make sense. So |
But one overload can be made with a delegate parameter. public class MinimalActionEndpointRouteBuilderExtensions
{
public static void IncludeRouteGroup(this IEndpointRouteBuilder endpoints, string pattern, RouteGroup routeGroup);
public static void IncludeRouteGroup(this IEndpointRouteBuilder endpoints, string pattern, RouteGroup routeGroup, Func<HttpContext, Func<Task>, Task> onMatch);
} The onMatch will be executed whenever URL for the route group will match. |
Do we really need onMatch? What's that for? |
I won't say we really need that. But there might be a situation where we may want to execute a common piece of code for a group route. Maybe some logging. |
public class EndpointRouteBuilderExtensions
{
public static void MapGroup(this IEndpointRouteBuilder endpoints, string pattern, Action<IApplicationBuilder> configureApp, Action<IEndpointRouteBuilder> configureRoutes);
public static void MapGroup(this IEndpointRouteBuilder endpoints, string pattern, Action<IEndpointRouteBuilder> configureRoutes);
} app.MapGroup("/api/users", static app => app.UseBuffering(), routes => routes.MapGet("/", (TodoDb db) => db.ToListAsync()));
app.MapGroup("/api/users", routes => routes.MapGet("/", (TodoDb db) => db.ToListAsync())); |
MapGroup needs us to give a delegate parameter that will configure the routes. I'm not really convinced with that. I would like if MapGroup returns an IEndpointRouteBuilder instance and we will configure routes on that instance or it will take a parameter of type RouteGroup and we will configure route on that RouteGroup. public class EndpointRouteBuilderExtensions
{
public static IEndpointRouteBuilder MapGroup(this IEndpointRouteBuilder endpoints, string pattern);
} var group = app.MapGroup("/api/users");
group.MapGet("/", (TodoDb db) => db.ToListAsync()); or public class EndpointRouteBuilderExtensions
{
public static IEndpointRouteBuilder MapGroup(this IEndpointRouteBuilder endpoints, string pattern, RouteGroup group);
} RouteGroup group = new();
group.MapGet("/", (TodoDb db) => db.ToListAsync());
app.MapGroup("/api/users", group); |
I like the first one: var group = app.MapGroup("/api/users");
group.MapGet("/", (TodoDb db) => db.ToListAsync()); Can you do things like apply conventions to entire groups? For example: var group = app.MapGroup("/api/users");
group.RequireAuthentication();
group.MapGet("/", (TodoDb db) => db.ToListAsync()); |
Yes, I think we should be able to do that, |
@ziaulhasanhamim check out https://github.com/khalidabuhakmeh/branchy It kind of works like what you're doing. The approach I took was to call MapXXX with each call rather than wait to register them at a later time (https://github.com/khalidabuhakmeh/Branchy/blob/main/src/Branchy/NestedEndpointConventionBuilder.cs). I also haven't added any functionality to add metadata to groups. While it sounds good in theory, the only metadata I could think of that might be useful is authorization/authentication. I also recommend checking out the .NET 6 branch of Carter. |
@khalidabuhakmeh yeah your approach is great. But for me, the router option seems more robust. But I'm not saying your approach is not useful. Your approach is absolutely correct and useful. |
I would like to have that in .net 6. it will make minimal action actually useful to our company and our developers |
We're too late in the cycle to design anything that will be a part of .NET 6 now. I'd suggesting using @khalidabuhakmeh's package mentioned above to get a feel for what the API could look like. |
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
This should be a .NET 7 candidate. |
I have created a simple code if you want to try it on .NET 6 var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var group = app.MapGroup("/group/{message1}");
group.MapGet("{message2}", (string message1, string message2) => $"{message1} {message2}");
app.Run();
public static class MapGroupExtensions
{
public static MapGroupBuilder MapGroup(this IEndpointRouteBuilder app, string groupPattern)
{
return new MapGroupBuilder(app, groupPattern);
}
}
public class MapGroupBuilder
{
private IEndpointRouteBuilder _app;
private string _groupPattern;
public MapGroupBuilder(IEndpointRouteBuilder app, string groupPattern)
{
_app = app;
_groupPattern = groupPattern;
}
public IEndpointConventionBuilder MapGet(string pattern, Delegate handler)
{
string requestPattern = CombinePattern(pattern);
return _app.MapGet(requestPattern, handler);
}
public IEndpointConventionBuilder MapPost(string pattern, Delegate handler)
{
string requestPattern = CombinePattern(pattern);
return _app.MapPost(requestPattern, handler);
}
public IEndpointConventionBuilder MapPut(string pattern, Delegate handler)
{
string requestPattern = CombinePattern(pattern);
return _app.MapPut(requestPattern, handler);
}
public IEndpointConventionBuilder MapDelete(string pattern, Delegate handler)
{
string requestPattern = CombinePattern(pattern);
return _app.MapDelete(requestPattern, handler);
}
private string CombinePattern(string pattern)
{
return _groupPattern.TrimEnd('/') + "/" + pattern.TrimStart('/');
}
} |
Thanks for contacting us. We're moving this issue to the |
Reopening to track #41367 |
Background and Motivation
The new minimal approach in asp.net core 6 is good. I mean it's great. But I don't this will be useful in a real web application. Because this doesn't have an easy way to integrate route grouping. With route grouping, minimal actions can be separated into multiple files easily with the help of normal or static(since c# doesn't support modules like f#) classes without using MVC or Razor pages.
Proposed API
Usage Examples
You can separate these route actions to multiple files easily. RouteGroup would become more useful if in the future c# gets support for top-level functions(not just for Program.cs).
The text was updated successfully, but these errors were encountered: