In the last post, we talked about what Middleware is, what it’s for, and simple ways of including it in our ASP.NET 6 app’s pipeline.
In this post, we’re going to expand on these fundamentals to build a few custom Middleware classes.
Standard Middleware Architecture
Unlike what we did in Part 1, most of the time we want to have our Middleware be separate classes, and not just additional lines in our Program.cs file.
Remember this middleware from Part 1? The one that returned a response consisting of “Hello world”?
Let’s create a custom middleware class that does the same thing.
Here’s a basic empty class we’ll use for this middleware:
A middleware class consists of three parts. First, any middleware class in ASP.NET 6 must include a private instance of RequestDelegate
which is populated by the class’s constructor. Remember that RequestDelegate
represents the next piece of middleware in the pipeline:
Second, the class must have an async method InvokeAsync()
which takes an instance of HttpContext
as its first parameter.
Third, the middleware must have its own unique implementation. For this middleware, all we want to do is return a custom response:
NOTE: within the InvokeAsync()
method, most middleware will have a call to await next(context);
any middleware that does not do this will be a terminal middleware, because the pipeline beyond that point will not be run. Since our SimpleResponseMiddleware
does, in fact, want to stop the pipeline processing, it does not call await next(context)
.
Adding Middleware to the Pipeline
At this point, it is possible for us to wire up this middleware class to our app in the Program.cs file using the UseMiddleware<T>()
method:
For very simple middleware classes, this is sufficient. However, often we use extension methods instead of UseMiddleware<T>()
because they provide us with another layer of abstraction:
We would then use that extension method like so:
Either way of doing this is correct. I personally prefer the clarity and readability of the extension method route, but you can go with whatever you like better.
Building a Logging Middleware
One of the most common scenarios for middleware is logging, specifically logging of things like the request path or headers, culture, or response body.
The LoggingService Class
We’re going to build a logging middleware class that does two things:
- Log the request path.
- Log the distinct response headers.
First, we must create an interface ILoggingService
and class LoggingService
.
We then need to add LoggingService
to the Services collection of the app in Program.cs:
Middleware classes can have services injected into them, just like normal classes.
The LoggingMiddleware Class
Now we need a LoggingMiddleware
class which actually does the logging. First, let’s create a skeleton for the class LoggingMiddleware
, which accepts an instance of ILoggingService
as a parameter in the constructor:
Remember that we are recording the request’s path, and the response’s unique headers. That means we will have code on either side of the call to await next(context)
.
Adding LoggingMiddleware to the Pipeline
Finally, we need to call our new extension method in Program.cs to add the LoggingMiddleware
class to the pipeline:
When we run the app, we can see (by setting breakpoints) that the code will correctly log both the request path and the response headers.
Pingback: ASP.NET 6 Middleware - Pradeep Barli