Implement Middlewares Using Endpoint Routing in ASP.NET Core 3.0
ASP.NET Core introduced some new ways to work with middleware in your web applications. Read on to learn more about it from an expert!
Join the DZone community and get the full member experience.
Join For FreeIf you have a middleware that needs to work on a specific path, you should implement it by mapping it to a route in ASP.NET Core 3.0, instead of just checking the path names. This post doesn't handle regular middlewares, which need to work all request, or all requests inside a Map
or MapWhen
branch.
At the Global MVP Summit 2019 in Redmond I attended the hackathon where I worked on my GraphQL middlewares for ASP.NET Core. I asked Glen Condron for a review of the API and the way the middleware gets configured. He told me that we did it all right. We followed the proposed way to provide and configure an ASP.NET Core middleware. But he also told me that there is a new way in ASP.NET Core 3.0 to use this kind of middleware.
Glen asked James Newton King, who works on the new Endpoint Routing, to show me how this needs to be done in ASP.NET Core 3.0. James pointed me to the ASP.NET Core Health Checks and explained to me the new way to go.
BTW: That's kinda closing the loop: Four summits ago Damien Bowden and I where working on the initial drafts of the ASP.NET Core Health Checks together with Glen Condron. Awesome that this is now in production!
The new ASP.NET Core 3.0 implementation of the GraphQL middlewares is in the aspnetcore30 branch of the repository: https://github.com/JuergenGutsch/graphql-aspnetcore
About Endpoint Routing
The MVP fellow Steve Gordon had an early look into Endpoint Routing. His great post may help you to understand Entpoint Routing.
How it Worked Before
Until now, you used MapWhen()
to map the middleware to a specific condition defined in a predicate:
Func<HttpContext, bool> predicate = context =>
{
return context.Request.Path.StartsWithSegments(path, out var remaining) &&
string.IsNullOrEmpty(remaining);
};
return builder.MapWhen(predicate, b => b.UseMiddleware<GraphQlMiddleware>(schemaProvider, options));
(ApplicationBuilderExtensions.cs)
In this case, the path is checked. It is pretty common to not only map based on paths. This allows you to also map on all other kinds of criteria based on the HttpContext
.
Also the much simpler Map()
was a way to go:
builder.Map(path, branch => branch.UseMiddleware<GraphQlMiddleware>(schemaProvider, options));
How This Should Be Done Now
In ASP.NET Core 3.0, these kinds of mappings, where you may listen on a specific endpoint, should be done using the EndpoiontRouteBuilder
. If you create a new ASP.NET Core 3.0 web application, MVC is now using a slightly different process in the Startup.cs
than before:
app.UseRouting(routes =>
{
routes.MapControllerRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRazorPages();
});
The method MapControllerRoute()
adds the controller based MVC and Web API. The new ASP.NET Core Health Checks, which also provide their own endpoint, will be added like this. This means we now have Map()
methods as extension methods on the IEndpointRouteBuilder
instead of Use()
methods on the IApplicationBuilder
. It is still possible to use the Use
methods.
In case of the GraphQL middleware, it looks like this:
var pipeline = routes.CreateApplicationBuilder()
.UseMiddleware<GraphQlMiddleware>(schemaProvider, options)
.Build();
return routes.Map(pattern, pipeline)
.WithDisplayName(_defaultDisplayName);
(EndpointRouteBuilderExtensions.cs)
Based on the current IEndpointRouteBuilder
a new IApplicationBuilder
is created, where we Use
the GraphQL Middleware as before. We pass the ISchemaProvider
and the GraphQlMiddlewareOptions
as arguments to the middleware. The result is a RequestDelegate
in the pipeline
variable.
The configured endpoint pattern
and the pipeline
than gets mapped to the IEndpointRouteBuilder
. The small extension Method WithDisplayName()
sets the configured display name to the endpoint.
I needed to copy this extension method from the ASP.NET Core repository to my code base, because the current development build of ASP.NET Core didn't contain this method two weeks ago. I need to check the latest version ASAP.
In ASP.NET Core 3.0, the GraphQL and the GraphiQl middleware can now added like this:
app.UseRouting(routes =>
{
if (env.IsDevelopment())
{
routes.MapGraphiQl("/graphiql");
}
routes.MapGraphQl("/graphql");
routes.MapControllerRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRazorPages();
});
Conclusion
The new ASP.NET Core 3.0 implementation of the GraphQL middlewares is on the aspnetcore30 branch of the repository: https://github.com/JuergenGutsch/graphql-aspnetcore
This approach feels a bit different. In my opinion, it messes with the startup.cs
a little bit. Previously, we added one middleware after another, line by line to the IApplicationBuilder
method. With this approach we have some middlewares still registered on the IApplicationBuilder
and some others on the IEndpointRouteBuilder
inside a lambda expression on a new IApplicationBuilder
.
The other thing is that the order isn't really clear anymore. When will the middlewares inside the UseRouting()
method be executed and in which direction? I will dig deeper into this in the coming months.
Published at DZone with permission of Juergen Gutsch, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments