Middleware
HTTP Request Pipeline
When an HTTP request is received, it enters the web API and goes through a series of processes known as the HTTP request pipeline.
A pipe is a chain of processes connected in such a way that the output of one element becomes the input of the next.
The request pipeline consists of a set of connected processes that receive an HTTP request, process it, and return some kind of result. One of these processes is the MVC process, where controllers and actions are handled.
Each process in the pipeline is referred to as middleware.
Another important middleware is the authorization middleware, which determines whether a user has the necessary permissions to access a specific resource. It’s common to configure permissions within the MVC process, where we specify that an action can only be consumed by users with the appropriate roles or claims. For this authorization to work, the request must pass through the authorization middleware before reaching the MVC middleware.
This highlights an important aspect: the order of the processes in the pipeline is crucial. For example, the authorization middleware must come before the MVC middleware to ensure that users are authenticated and authorized before accessing the controllers.
The request pipeline is configured in the Program
class (or the Configure
method in the Startup
class).
Example:
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization(); // Authorization middleware
app.MapControllers(); // MVC middleware
app.Run();
Here, app.Environment
allows us to configure which middleware runs in different environments. In this example, Swagger is only enabled in the development environment.
Short Circuit
A process can stop the execution of the pipeline by returning a response early, without passing control to the remaining processes. This is known as short-circuiting the pipeline.
For example, add the following code at the start of the pipeline:
app.Run(async context =>
{
await context.Response.WriteAsync("Short Circuiting");
});
No matter which endpoint is requested, it will always show this message:
Customizing Short Circuit for a Specific Route
To short-circuit only a specific route, you can use app.Map
to branch the pipeline based on the URL:
app.Map("/test", (app) =>
{
app.Run(async context =>
{
await context.Response.WriteAsync("Short Circuiting");
});
});
This allows you to control the flow of the pipeline based on specific routes or URLs.
Running a Process Without Short Circuiting
To run a process without short-circuiting the pipeline (i.e., allowing the next middleware to execute), use app.Use
as you would with UseHttpsRedirection()
or UseAuthorization()
.
For example, to log all HTTP responses to the console:
app.Use(async (context, next) =>
{
var logger = app.Logger;
using (var swapStream = new MemoryStream())
{
var originalResponseBody = context.Response.Body;
context.Response.Body = swapStream;
await next.Invoke(); // Allow the next middleware to execute
// After the rest of the pipeline finishes executing & receives a response
// Since we arent allows to read the response body, create a temp one to copy
swapStream.Seek(0, SeekOrigin.Begin); // Seek from the beginning
string responseBody = new StreamReader(swapStream).ReadToEnd(); // Capture everything
swapStream.Seek(0, SeekOrigin.Begin); // Back to the beginning
// Copy the response back to the original body
await swapStream.CopyToAsync(originalResponseBody);
context.Response.Body = originalResponseBody;
// Log the response body to the console
logger.LogInformation(responseBody);
};
});