ASP.NET Core 2.0 Middleware

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,

  1. public void Configure(  
  2.     IApplicationBuilder app,  
  3.     IHostingEnvironment env)  
  4. {  
  5.     // setup request pipeline using middleware  
  6.     app.Run(async (context) =>  
  7.     {  
  8.         await context.Response.WriteAsync("Hello World! (Run)");  
  9.     });  
  10. }  

It is a good practice to use extension methods on IApplicationBuilder to build the pipeline,

  1. // RunMiddlewareExtensions.cs  
  2.         public static void RunHelloWorld(this IApplicationBuilder app)  
  3.         {  
  4.             app.Run(async (context) =>  
  5.             {  
  6.                 await context.Response.WriteAsync("Hello World! (Run)");  
  7.             });  
  8.         }  
  9.         // Startup.cs  
  10.         public void Configure(  
  11.             IApplicationBuilder app,  
  12.             IHostingEnvironment env)  
  13.         {  
  14.             // setup request pipeline using middleware  
  15.             app.RunHelloWorld();  
  16.         }  

In the previous snippet we used IApplicationBuilder.Run() to configure middleware, another way to do this is IApplicationBuilder.Use(),

  1. // UseMiddlewareExtensions.cs  
  2. public static IApplicationBuilder UseHelloWorld(  
  3.     this IApplicationBuilder app)  
  4. {  
  5.     return app.Use(async (context, next) =>  
  6.     {  
  7.         await context.Response.WriteAsync("Hello World! (Use)\n");  
  8.         await next();  
  9.     });  
  10. }  
  11. // Startup.cs  
  12. public void Configure(  
  13.     IApplicationBuilder app,  
  14.     IHostingEnvironment env)  
  15. {  
  16.     // setup request pipeline using middleware  
  17.     app.UseHelloWorld();  
  18.     app.RunHelloWorld();  
  19. }  

It is a good practice to have middleware components defined in a separate class,

  1. public class HelloWorldMiddleware  
  2. {  
  3.     private readonly RequestDelegate next;  
  4.   
  5.     public HelloWorldMiddleware(RequestDelegate next)  
  6.     {  
  7.         this.next = next;  
  8.     }  
  9.   
  10.     public async Task Invoke(HttpContext context)  
  11.     {  
  12.         await context.Response.WriteAsync("Hello World! (Use in Class)\n");  
  13.         await this.next(context);  
  14.     }  
  15. }  
  16. // UseMiddlewareExtensions.cs  
  17. public static IApplicationBuilder UseHelloWorldInClass(  
  18.    this IApplicationBuilder app)  
  19. {  
  20.       return app.UseMiddleware<HelloWorldMiddleware>();  
  21. }  
  22. // Startup.cs  
  23. public void Configure(  
  24.         IApplicationBuilder app,  
  25.         IHostingEnvironment env)  
  26. {  
  27.     // setup request pipeline using middleware  
  28.     app.UseHelloWorld();  
  29.     app.UseHelloWorldInClass();  
  30.     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,

  1. Run() - adds a middleware and ends the pipeline i.e. doesn’t call next middleware.
  2. Use() - adds a middleware, as a lambda or a dedicated class.
  3. 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,

  1. Its constructor accepts RequestDelegate. This will be called to forward the request to next middleware.
  2. 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