ASP.NET Core is a flexible, open-source web framework for building modern, scalable web applications. One of its key features is support for middleware, which provides a way to process requests and responses in a pipeline.
In ASP.NET Core, middleware is a component that’s composed of one or more delegate functions. Each delegate function can perform some processing on the incoming request and pass control to the next delegate function in the pipeline, or it can handle the request and produce a response. The response that is produced, flows back through the pipeline, giving each middleware component the opportunity to perform post-processing on the response.
This mechanism provides a flexible and extensible way of handling HTTP requests and responses, making it easy to implement complex features such as authentication, logging, and routing.
Some of the middleware that can be implemented in an ASP.NET Core application include:
1. Authentication Middleware: This provides authentication functionality for the application, such as handling login and logout functionality.
2. Static Files Middleware: This serves static files, such as images, CSS, and JavaScript files, to the client.
3. Routing Middleware: This maps incoming requests to the appropriate action in a controller.
4. Session Middleware: This provides a way to store user-specific data between requests, such as user preferences or shopping cart contents.
5. Exception Handling Middleware: This provides a way to handle exceptions that occur during request processing and produce a friendly error response.
6. CORS (Cross-Origin Resource Sharing) Middleware: This provides a way to handle cross-domain requests by allowing or denying specific origins.
7. GZIP Compression Middleware: This compresses the response payload to reduce the amount of data transferred over the network, improving performance.
8. HTTPS Redirection Middleware: This redirects HTTP requests to HTTPS to ensure that sensitive data is transmitted securely.
Architecture of Middleware
The architecture of Middleware in ASP.NET Core consists of a pipeline of components that handle incoming HTTP requests and outgoing responses. Each middleware component in the pipeline is responsible for performing a specific task, such as authentication, logging, or routing.
The middleware pipeline in ASP.NET Core is constructed using the IApplicationBuilder
interface, which provides a fluent API for adding middleware components to the pipeline. The pipeline is constructed in Program.cs , which is executed when the application starts.
When an HTTP request is received by the ASP.NET Core application, it is processed by the middleware pipeline in the following way:
1. The request is passed to the first middleware component in the pipeline.
2. The middleware component performs its specific task, such as logging or authentication.
3. The middleware component can then either pass the request on to the next middleware component in the pipeline, or it can short-circuit the pipeline and return a response directly to the client.
4. The process is repeated for each middleware component in the pipeline until a response is returned to the client.
In ASP.NET Core 6.0, the Program.cs file contains code that gets executed when the application is started. It is used to configure the ASP.NET platform.
var app = builder.Build();
app.UseMiddleware<LoggingMiddleware>();
app.UseMiddleware<AuthenticationMiddleware>();
app.UseMiddleware<RoutingMiddleware>();
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseMiddleware<CorsMiddleware>();
app.UseMiddleware<GzipCompressionMiddleware>();
app.UseMiddleware<HttpsRedirectionMiddleware>();
app.MapControllers();
In this example, each middleware component is added to the pipeline using the UseMiddleware<T>
method on the IApplicationBuilder
interface. The order in which the middleware components are added is important, as each component may depend on the functionality provided by the previous components in the pipeline.
One of the key improvements is the introduction of a new generic IMiddleware
interface, which provides a simpler and more streamlined way of creating middleware components. The IMiddleware
interface has a single method called InvokeAsync
, which is responsible for handling the HTTP request and generating the response.
Here is an example of a middleware component that implements the IMiddleware
interface:
public class LoggingMiddleware : IMiddleware
{
private readonly ILogger<LoggingMiddleware> _logger;
public LoggingMiddleware(ILogger<LoggingMiddleware> logger)
{
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
_logger.LogInformation("Request received: {Method} {Path}", context.Request.Method, context.Request.Path);
await next(context);
_logger.LogInformation("Response sent: {StatusCode}", context.Response.StatusCode);
}
}
In this example, the LoggingMiddleware
component implements the IMiddleware
interface and provides a simple way of logging incoming requests and outgoing responses. The InvokeAsync
method is responsible for logging the request, calling the next middleware component in the pipeline, and logging the response.
The result of the Build() method is a WebApplication object, which can be used to set up middleware components.
Key Components in the Middleware
Let’s take a closer look at some of the key components involved in the LoggingMiddleware example we just saw.
First, there’s the HttpContext
object, which represents the context of an HTTP request and provides access to information about the request and response. The HttpContext
object is passed from one middleware component to another middleware component as the request travels through the pipeline.
Next, there’s the RequestDelegate
delegate object, which invoke a function that represents the next middleware component in the pipeline. When a middleware component is finished processing a request, it can either pass the request on to the next middleware component by invoking the RequestDelegate
, or it can generate a response and return it directly to the client.
The IMiddleware
interface is used to define middleware components in ASP.NET Core. Middleware components implement this interface and provide a single InvokeAsync
method, which is responsible for processing incoming requests and generating responses.
To add middleware components to the pipeline, developers can use the Use
method provided by the IApplicationBuilder
interface. This method takes an instance of a middleware component and adds it to the pipeline.
Finally, it’s worth noting that middleware components in ASP.NET Core can be configured using options. Options are classes that define settings and parameters that can be used to configure middleware components. Developers can use the IOptions
interface to inject these options into their middleware components and use them to customize their behavior.
Advantages of the Middleware architecture
The middleware architecture in ASP.NET Core provides a powerful and flexible framework for processing HTTP requests and generating responses. It’s designed to be extensible and customizable, allowing developers to create their own middleware components and configure them to suit their needs.
One of the key benefits of the middleware architecture in ASP.NET Core is its modularity. Because each middleware component is responsible for a single, specific task, developers can easily swap out components or add new ones as needed. This makes it easy to build complex web applications with a flexible and modular architecture.
Another advantage of the middleware architecture is that it provides a clear separation of concerns (SoC). Each middleware component is responsible for a specific aspect of request processing, such as authentication, routing, or response compression. This makes it easier to reason about the code and ensure that each component is doing its job correctly.
The middleware architecture in ASP.NET Core also provides a high degree of performance and scalability. Because each middleware component is lightweight and focused on a specific task, it can process requests quickly and efficiently. And because the pipeline is configurable, developers can optimize it for their specific use case to ensure maximum performance.
Configuring the Middleware Pipeline
An important aspect of middleware in ASP.NET Core is the ability to configure the middleware pipeline. Middleware can be added, removed, or reordered to meet the specific needs of an application. The order of the middleware components in the pipeline is significant, as it determines the processing order of the requests and responses.
For example, authentication middleware typically needs to be placed early in the pipeline, so that the authentication information can be used by other middleware components. Similarly, exception handling middleware should be placed near the end of the pipeline, so that it can catch any exceptions that occur during request processing.
In addition to the built-in middleware components, it’s also possible to create custom middleware components. Custom middleware can be used to implement application-specific functionality, such as logging or performance monitoring. To create custom middleware, you simply implement a delegate function that performs the desired processing and add it to the middleware pipeline.
Custom Middleware has been discussed in another article Custom middleware in an ASP.NET Core application..
Examples of Middleware Components
At the beginning of the article, we briefly touched upon some of the Middleware that can be implement in an ASP.NET Core application. These were and are not limited to:
- Authentication Middleware
- Static Files Middleware
- Routing Middleware
- Session Middleware
- Exception Handling Middleware
- CORS (Cross-Origin Resource Sharing) Middleware
- GZIP Compression Middleware
- HTTPS Redirection Middleware
Let us explore each of these and see how to implement implement each of the middleware components in ASP.NET Core. The examples here target ASP.NET Core 5.0 which can easily be upgraded to v6 or higher by moving the code to Program.cs.
1. Authentication Middleware:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
app.UseAuthentication();
This example uses the CookieAuthentication
scheme and adds it to the IServiceCollection
in the ConfigureServices
method. The UseAuthentication
method is then called in the Configure
method to add the authentication middleware to the pipeline.
2. Static Files Middleware:
app.UseStaticFiles();
The UseStaticFiles
method adds the static files middleware to the pipeline, allowing the application to serve static files, such as images, CSS, and JavaScript files, to the client.
3. Routing Middleware:
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
The UseRouting
method adds the routing middleware to the pipeline, and the UseEndpoints
method sets up the endpoint routing for the application. In this example, a default route is defined that maps incoming requests to a controller action.
4. Session Middleware:
using Microsoft.AspNetCore.Session;
builder.Services.AddSession();
app.UseSession();
The AddSession
method is called in the ConfigureServices
method to add the session services to the IServiceCollection
, and the UseSession
method is called in the Configure
method to add the session middleware to the pipeline.
5. Exception Handling Middleware:
app.UseExceptionHandler("/Error");
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
// Log the exception
// ...
context.Response.StatusCode = 500;
await context.Response.WriteAsync("An error occurred, please try again later.");
}
});
The UseExceptionHandler
method is called to add the exception handling middleware to the pipeline, and a custom delegate function is used to catch exceptions and log them. In this example, a generic error message is returned to the client, but in a real-world scenario, a more detailed error page could be displayed.
6. CORS (Cross-Origin Resource Sharing) Middleware:
using Microsoft.Asp.NetCore.Cors;
builder.Services.AddCors();
app.UseCors(options => options.WithOrigins("http://example.com").AllowAnyMethod().AllowAnyHeader());
The `AddCors` method is called in the `ConfigureServices` method to add the CORS services to the `IServiceCollection`, and the `UseCors` method is called in the `Configure` method to add the CORS middleware to the pipeline. In this example, the CORS policy allows requests from `http://example.com` to use any HTTP method and any header.
7. GZIP Compression Middleware:
using Microsoft.AspNetCore.ResponseCompression;
builder.Services.AddResponseCompression(options =>
{
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json" });
options.Providers.Add<GzipCompressionProvider>();
});
app.UseResponseCompression();
The `AddResponseCompression` method is called in the `ConfigureServices` method to add the response compression services to the `IServiceCollection`, and the `UseResponseCompression` method is called in the `Configure` method to add the response compression middleware to the pipeline. In this example, the `GzipCompressionProvider` is used to compress responses, and the compression is applied to `application/json` content types.
8. HTTPS Redirection Middleware:
app.UseHttpsRedirection();
The `UseHttpsRedirection` method is called in the `Configure` method to add the HTTPS redirection middleware to the pipeline. This middleware redirects incoming HTTP requests to HTTPS, ensuring that all communication between the client and server is encrypted.
These are just a few examples of the many middleware components that can be implemented in an ASP.NET Core application. By combining and configuring these components, you can create a highly customized and scalable application.
Middleware – Differences between ASP.NET Framework and ASP.NET Core
The concept of middleware in ASP.NET Framework and ASP.NET Core is similar in that it involves a pipeline of components that handle incoming requests and outgoing responses. However, there are several differences between the two frameworks in how middleware is implemented and used.
Hosting model: ASP.NET Framework runs on top of IIS (Internet Information Services), which provides hosting capabilities and process management. In contrast, ASP.NET Core is designed to be cross-platform and runs on top of a built-in web server called Kestrel, which can be hosted inside or outside of IIS.
Dependency Injection: ASP.NET Core includes a built-in dependency injection framework that is used to manage the lifecycle of middleware components and other services. This makes it easy to configure and manage the middleware pipeline using dependency injection.
Middleware pipeline: In ASP.NET Framework, middleware components are typically implemented as HTTP modules that are configured in the web.config file. In ASP.NET Core, middleware components are implemented as classes that are added to the middleware pipeline using the UseMiddleware
method in the Startup
class (or Program.cs for ASP.NET Core v6.0 and beyond).
Extensibility: ASP.NET Core’s middleware pipeline is highly extensible, making it easy to create and add custom middleware components to the pipeline. This is achieved using the IApplicationBuilder
interface, which provides a fluent API for adding and configuring middleware components.
Performance: ASP.NET Core is designed with performance in mind and includes several features that improve performance, such as the use of Kestrel as a web server, optimized routing, and support for asynchronous programming. These features make it possible to handle large numbers of requests with high throughput and low latency.
Cross-platform support: ASP.NET Core is designed to be cross-platform and can run on different operating systems such as Windows, Linux, and macOS. This means that developers can create and deploy ASP.NET Core applications (including middlware) on different platforms without any issues.
Configuration: ASP.NET Core provides a flexible configuration system that allows developers to configure their application using various sources such as appsettings.json, environment variables, and command-line arguments. Middleware components can also be configured using the configuration system.
Request/response objects: In ASP.NET Core, the HttpRequest
and HttpResponse
objects are designed to be lightweight and provide a simple API for working with incoming requests and outgoing responses. This makes it easier to implement middleware components that modify the request or response.
Middleware reuse: ASP.NET Core middleware components can be easily reused across different applications and can also be packaged as NuGet packages for easy distribution and reuse. This encourages code reuse and modularity, making it easier to develop and maintain complex web applications.
In summary, the concept of middleware in ASP.NET Core is more flexible, extensible, and efficient compared to ASP.NET Framework. It provides a highly modular and customizable pipeline for handling HTTP requests and responses, making it easier to create and maintain modern web applications.
Migrating HTTP handlers and modules to ASP.NET Core middleware
If you’re migrating an existing ASP.NET application to ASP.NET Core, you may need to migrate your HTTP handlers and modules to ASP.NET Core middleware. Fortunately, this process is relatively straightforward, although it does require some knowledge of both ASP.NET and ASP.NET Core.
We’ll explore how to migrate HTTP Handlers and modules to ASP.NET Core middleware in an upcoming article.
Conclusion
Overall, the middleware architecture in ASP.NET Core 6 is a powerful and flexible framework for building web applications. Whether you’re building a simple website or a complex web application, the middleware architecture provides the tools you need to build a flexible, modular, and scalable architecture that can adapt to your needs over time.
This article was technically reviewed by Mahesh Sabnis
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of
DotNetCurry,
DNC Magazine for Developers,
SQLServerCurry and
DevCurry. He has also authored a couple of books
51 Recipes using jQuery with ASP.NET Controls and
The Absolutely Awesome jQuery CookBook.
Suprotim has received the prestigious Microsoft MVP award for Sixteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.
Get in touch with him on Twitter @suprotimagarwal or at LinkedIn