NashTech Blog

Exploring Middleware in ASP.NET Core

Table of Contents

Middleware is a crucial concept in ASP.NET Core applications. It refers to the software components that are assembled into an application’s pipeline to handle requests and responses. Each component in the middleware pipeline has the opportunity to process an HTTP request, perform tasks, or pass control to the next middleware component in the pipeline.

What is Middleware ?

Middleware is a piece of code in an application pipeline used to handle requests and responses. In ASP.NET Core, middleware components are used to process HTTP requests and responses. For example, we may have a middleware component to authenticate a user, another piece of middleware to handle errors and another middleware to serve static files such as JavaScript files, CSS files, images, etc.

The following figure illustrates how a request processes through middleware components.

Middleware can be built-in as part of the .NET Core framework, added via NuGet packages, or can be custom middleware. The configure method in the application startup class configures these middleware components. Configure methods set up a request processing pipeline for an ASP.NET Core application. It consists of a sequence of request delegates called one after the other.

In general, each middleware may handle the incoming requests and pass execution to the next middleware for further processing.

However, a middleware component can decide to not call the next piece of middleware in the pipeline. This action terminates the request pipeline, a process known as short-circuiting. Short-circuiting is desirable because it avoids unnecessary work. For example, if the request is for a static file like an image CSS file, JavaScript file, etc., these static files middleware can handle and serve that request and then short-circuit the rest of the pipeline.

Example

  1. Let’s understand it with an example and observe the default configuration of middleware in the Configure method of the Startup class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
// This middleware is used to report app runtime errors in a development environment.
app.UseDeveloperExceptionPage();
}
else
{
// This middleware catches exceptions thrown in a production environment.
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios; see https://aka.ms/aspnetcore-hsts.
app.UseHsts(); // Adds the Strict-Transport-Security header.
}

// This middleware is used to redirects HTTP requests to HTTPS.
app.UseHttpsRedirection();

// This middleware is used to returns static files and short-circuits further request processing.
app.UseStaticFiles();

// This middleware is used to route requests.
app.UseRouting();

// This middleware is used to authorizes a user to access secure resources.
app.UseAuthorization();

// This middleware is used to add Razor Pages endpoints to the request pipeline.
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}

Creating a custom Middleware

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

public class Startup
{
public void Configure(IApplicationBuilder app)
{
// Add middleware to the pipeline
app.UseMiddleware<LoggingMiddleware>();
app.UseMiddleware<AuthenticationMiddleware>();

// Configure routing
app.UseRouting();

// Configure endpoints
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}

public class LoggingMiddleware
{
private readonly RequestDelegate _next;

public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task Invoke(HttpContext context)
{
// Log request information
// Example: Console.WriteLine($"Request: {context.Request.Path}");

// Call the next middleware in the pipeline
await _next(context);
}
}

public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;

public AuthenticationMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task Invoke(HttpContext context)
{
// Perform authentication logic
// Example: if (!context.User.Identity.IsAuthenticated) { ... }

// Call the next middleware in the pipeline
await _next(context);
}
}

Ordering of Middleware

The order of Middleware components matters as each Middleware can influence the behavior of subsequent components. For example, if authentication Middleware is added before routing Middleware, authentication will be performed before routing the request to the appropriate controller action.

To control the order of Middleware execution, we can use the Use and Run extension methods. The Use method adds the Middleware to the pipeline, while the Run method adds a terminal Middleware that doesn’t call the next Middleware.

Here is an example :

public void Configure(IApplicationBuilder app)
{
// First middleware component
app.UseMiddleware<FirstMiddleware>();

// Second middleware component
app.UseMiddleware<SecondMiddleware>();

// Third middleware component
app.UseMiddleware<ThirdMiddleware>();

// Other middleware or endpoint routing
app.UseRouting();

// Other configurations...

// Terminal middleware using app.Run

app.Run(async context => {

// Handle the incoming request

await context.Response.WriteAsync("Hello from terminal middleware!"); });

}

In this example, FirstMiddleware will execute before SecondMiddleware, and SecondMiddleware will execute before ThirdMiddleware. We can continue adding middleware components in the desired order as needed.

Effective Use of Middleware in .NET Core Applications

  1. Moderate Use of Middleware: While middleware is a potent tool, it’s important to use it judiciously. Overloading the pipeline with too many middleware components can lead to slower application performance and increased maintenance complexity.
  2. Ordering Middleware: Arrange the middleware components in the pipeline in the correct order to ensure they execute as intended. Ensure that you add authentication middleware before authorization middleware to achieve the desired behavior.
  3. Error Handling: Middleware can be used for error handling at various levels of the request pipeline. You can create custom error handling middleware to catch exceptions and return appropriate error responses to clients. Additionally, consider using built-in middleware like UseExceptionHandler or UseStatusCodePages for handling errors.
  4. Testing Middleware: Test our middleware thoroughly to ensure it behaves as expected. Write unit tests and integration tests to validate the functionality and behavior of your middleware components under different scenarios.

Conclusion

ASP.NET Core Middleware is a powerful and flexible feature that enables developers to handle HTTP requests and responses in a modular and extensible manner. By creating custom Middleware components, we can add custom logic, modify requests, and process responses to build robust and feature-rich web applications.

Picture of Ajay Jajoo

Ajay Jajoo

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top