When building an ASP.NET Core application, you can draw on various middleware components to inspect, route, or modify the request and response messages that flow through the pipeline. You can also write your own custom middleware in ASP.NET Core.
Usually, you have a chain of middleware components in the application pipeline in ASP.NET Core. In this article, we’ll examine middleware in ASP.NET Core and how we can work with the convention-based and factory-based approaches.
To use the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here.
Create an ASP.NET Core 7 Web API project in Visual Studio 2022
First off, let’s create an ASP.NET Core 7 project in Visual Studio 2022. Follow these steps:
- Launch the Visual Studio 2022 IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “ASP.NET Core Web API” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window, specify the name and location for the new project.
- Optionally check the “Place solution and project in the same directory” check box, depending on your preferences.
- Click Next.
- In the “Additional Information” window shown next, leave the “Use controllers (uncheck to use minimal APIs)” box checked. We won’t be using minimal APIs in this project. Leave the “Authentication Type” set to “None” (the default).
- Ensure that the check boxes “Enable Open API Support,” “Configure for HTTPS,” and “Enable Docker” remain unchecked as we won’t be using those features here.
- Click Create.
We’ll use this ASP.NET Core 7 Web API project to work with factory-based middleware activation in the sections below.
Understanding middleware in ASP.NET Core
Middleware are the software components that comprise the request/response processing pipeline in ASP.NET Core. Incoming requests flow through each middleware component in the pipeline, and each of those components can either process the request or forward it to the next component in the pipeline.
There are lots of things middleware can do, including authentication, authorization, logging, exception handling, routing, caching, and response compression. You can modularize the functionality of your application into individual components and add, remove, or rearrange middleware to tailor the request and response processing pipeline to your application.
Traditionally, you had to configure your middleware components using the UseMiddleware extension method in the Startup class or the Program.cs file when using a newer version of ASP.NET Core. In contrast, factory-based middleware activation lets you define and configure middleware components using factories, which provide greater flexibility in activation.
Note that if you don’t write your custom middleware factory class, the default middleware factory will be used. In ASP.NET Core, you can activate your middleware in two different ways: convention-based middleware activation and factory-based middleware activation. Let’s now examine both approaches.
Convention-based middleware in ASP.NET Core
Convention-based middleware activation in ASP.NET Core is a feature that allows you to automatically apply middleware to the request/response pipeline based on predefined conventions, rather than explicitly configuring each middleware component. The following code listing illustrates how you can create a convention-based middleware component.
public class ConventionalMiddleware { private readonly RequestDelegate _next; public ConventionalMiddleware(RequestDelegate next) => _next = next; public async Task InvokeAsync(HttpContext context) { Trace.WriteLine("Inside the Conventional Middleware."); await _next(context); } }
You can add this middleware to the request processing pipeline usng the following piece of code in the Program.cs file.
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.UseMiddleware<ConventionalMiddleware>(); app.Run();
Factory-based middleware in ASP.NET Core
Factory-based middleware activation in ASP.NET Core provides a more flexible and dynamic way to configure and activate middleware components. With factory-based middleware activation, you can customize the middleware instantiation process based on your application’s needs.
Factory-based middleware allows you to inject dependencies having a scoped lifetime using the constructor of the your middleware class—a feature not supported by convention-based middleware. A scoped lifetime means that the service is created once per client request, and disposed at the end of the request.
Factory-based middleware has the following advantages over traditional-style or convention-based middleware:
- While convention-based middleware is created once when starting the ASP.NET Core application, factory-based middleware is created with each request.
- Because factory-based middleware provides per-request activation support, you can inject scoped services into the middleware’s constructor.
- Factory-based middleware promotes strong typing of the middleware type.
To use factory-based middleware activation, you should follow the four steps outlined below.
- Create a class that represents your middleware component and implements the IMiddleware interface.
- Implement the InvokeAsync method in your middleware that defines the logic for the middleware.
- Add the middleware to the DI container using the AddSingleton or AddScoped method.
- Configure the middleware pipeline by using the UseMiddleware extension method and specifying the type of your middleware component.
The following code listing illustrates how you can write a factory-based middleware component.
public class FactoryActivatedMiddleware : IMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { Trace.WriteLine("Inside the Factory Activated Middleware."); await next.Invoke(context); } }
You should register the middleware in the services container using the following piece of code.
var builder = WebApplication.CreateBuilder(args); builder.Services.AddTransient<FactoryActivatedMiddleware>();
You can now add this middleware much the same way we added middleware in the convention-based middleware example.
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.UseMiddleware<FactoryActivatedMiddleware>(); app.Run();
Each call to the UseMiddleware extension method checks whether or not the middleware implementation in question conforms to the IMiddleware interface. If this condition is met, the instance of IMiddlewareFactory registered in the service container is used to resolve the IMiddleware interface implementation instead of the convention-based middleware implementation. This middleware is registered as a transient or scoped service within the services container.
The IMiddlewareFactory interface defines two methods, namely the Create(Type) and Release(IMiddleware) methods. While the Create(Type) method is used to create a middleware instance for each request, the Release(IMiddleware) method releases an IMiddleware instance at the end of a request. The default implementation of the IMiddlewareFactory is available in the Microsoft.AspNetCore.Http.MiddlewareFactory class.