Problem
Create a Hello World
using ASP.NET Core Middleware.
Solution
Starting from the Empty Project from a previous post, amend the Configure()
method in Startup.cs as below:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World! (Run)");
});
}
It is a good practice to use extension methods on IApplicationBuilder
to build the pipeline:
public static void RunHelloWorld(this IApplicationBuilder app)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World! (Run)");
});
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env)
{
app.RunHelloWorld();
}
In the previous snippet, we used IApplicationBuilder.Run()
to configure middleware, another way to do this is IApplicationBuilder.Use()
:
public static IApplicationBuilder UseHelloWorld(
this IApplicationBuilder app)
{
return app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello World! (Use)\n");
await next();
});
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env)
{
app.UseHelloWorld();
app.RunHelloWorld();
}
It is a good practice to have middleware components defined in a separate class:
public class HelloWorldMiddleware
{
private readonly RequestDelegate next;
public HelloWorldMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync("Hello World! (Use in Class)\n");
await this.next(context);
}
}
public static IApplicationBuilder UseHelloWorldInClass(
this IApplicationBuilder app)
{
return app.UseMiddleware<HelloWorldMiddleware>();
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env)
{
app.UseHelloWorld();
app.UseHelloWorldInClass();
app.RunHelloWorld();
}
Discussion
Middleware is a component that intercepts HTTP request and response messages. We create a chain of these components to build a request pipeline for our application.
We setup this pipeline in Configure()
method via its IApplicationBuilder
parameter, that has the following methods for this purpose:
Run()
: Adds a middleware and ends the pipeline, i.e., doesn’t call next middleware
Use()
: Adds a middleware, as a lambda or a dedicated class
Map()
: Adds middleware based on request paths
Run
It takes RequestDelegate
delegate as a parameter, which when called has HttpContext
as its parameter. It returns void
because it short-circuits the request pipeline.
Use
It takes Func
as a parameter, i.e., takes in HttpContext
and a pointer to next middleware, and returns nothing (Task
). If the next middleware is not called, it short-circuits the request pipeline (same as Run
).
UseMiddleware
When setting up middleware as a class, we use UseMiddleware
to wire it up, providing our class as generic parameter.
The dedicated middleware class has two important pieces in it:
- Its constructor accepts
RequestDelegate
. This will be called to forward the request to next middleware.
- It has a method
Invoke
accepting HttpContext
and returning a Task
. This is called by the framework when calling the middleware.
Note: Implementing middleware in a dedicated class and wiring up using UseMiddleware
is the best/cleanest approach.
Extension Methods
Note the difference in extension methods, RunXXX
doesn’t return a value I however UseXXX
does (IApplicationBuilder
). This is because Run()
ends (short-circuits) the pipeline whereas Use()
may be chained with other middleware.
Order
Middleware components are called in the order they appear in Configure()
method, i.e., the order in which they are added to the pipeline. The response, on its way back to the client, also passes through the same middleware pipeline.