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 that takes in HttpContext and a pointer to the 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 a 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 Invokeaccepting 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're added to the pipeline. The response, on its way back to the client, also passes through the same middleware pipeline.
Source Code
GitHub