.NET Core Middleware Tutorial: Full Guide for Beginners | DotNetSchool

 Last Update:2025-06-23T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    9 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of ASP.NET Core Middleware Pipeline Configuration

What Is ASP.NET Core Middleware?

Middleware are software functions that are composed together to form an application's request processing pipeline. Each middleware component can perform tasks such as:

  • Inspect and modify the incoming HTTP requests.
  • Handle HTTP requests.
  • Invoke the next middleware component in the pipeline.
  • Inspect and modify the outgoing HTTP response.

Middleware Pipeline Structure

The middleware pipeline is a series of components where each component can:

  1. Process incoming requests.
  2. Modify or terminate the request.
  3. Pass the request to the next component in the pipeline.
  4. Process outgoing responses.

Key Points of Middleware Configuration

  1. Order Matters: The order in which you add middleware to the pipeline determines the sequence in which they execute on requests and reverse on responses. It's essential for correct functioning as some middlewares might depend on others (e.g., Authentication should be added before Authorization).

  2. Startup Class: Middleware configuration mainly happens in the Startup class, specifically in the Configure method. This method is where the HTTP request pipeline is set up.

  3. Use Methods: Middleware are added using methods like Use, UseWhen, MapWhen, Map, etc. They allow you to plug into the pipeline at different places and conditions.

Detailed Configuration Steps in the Startup Class

1. Create the Middleware

Before adding middleware to the pipeline, you need to create them. Middleware can be created by implementing the IMiddleware interface or by using a delegate (the latter is simpler for inline operations).

Example Using Delegate:

public static class SimpleHeaderMiddlewareExtensions
{
    public static IApplicationBuilder UseSimpleHeaderMiddleware(this IApplicationBuilder builder)
    {
        return builder.Use(async (context, next) =>
        {
            // Modify the HTTP request headers, e.g., adding a custom header
            context.Response.Headers.Add("X-Custom-Header", "CustomValue");

            // Call the next delegate/middleware in the pipeline
            await next();

            // Perform work after calling the next delegate/middleware
            await context.Response.WriteAsync("Request modified by SimpleHeaderMiddleware.\n");
        });
    }
}

Using IMiddleware Interface:

public class SimpleHeaderMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        context.Response.Headers.Add("X-Custom-Header", "CustomValue");
        await next(context);
    }
}

public static class SimpleHeaderMiddlewareExtensions
{
    public static IApplicationBuilder UseSimpleHeaderMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<SimpleHeaderMiddleware>();
    }
}

2. Configure the Middleware Pipeline

In your Startup class, use the Configure method to add middleware to the pipeline. Here’s a typical structure:

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add services here
        services.AddControllers();
        services.AddAuthentication(options => { ... });
        services.AddAuthorization(options => { ... });
        // ...
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        // Add Authentication Middleware
        app.UseAuthentication();

        // Add Authorization Middleware
        app.UseAuthorization();

        // Use Static Files (CSS, JS, Images, etc.)
        app.UseStaticFiles();

        // Use Cookie Policy Middleware
        app.UseCookiePolicy();

        // Use CORS Middleware
        app.UseCors("MyPolicy");

        // Custom Middleware
        app.UseSimpleHeaderMiddleware();

        // Add MVC Controllers or Endpoint routing middlewares as needed
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Important Middleware Used in ASP.NET Core

  • UseDeveloperExceptionPage(): Displays detailed information about a server exception that occurred during the execution of the pipeline.
  • UseExceptionHandler(): Handles exceptions that occur while processing an HTTP request.
  • UseHttpsRedirection(): Redirects HTTP requests to HTTPS.
  • UseRouting(): Enables endpoint routing, which matches incoming requests against a set of endpoints.
  • UseAuthorization(): Checks to see if the current user is authorized to access the requested resource.
  • UseAuthentication(): Enables authentication capabilities within the pipeline.
  • UseCors(): Configures CORS policies.
  • UseStaticFiles(): Allows serving static files like HTML, CSS, JavaScript, etc.
  • UseEndpoints(...): Maps endpoints to the request pipeline.
  • UseSession(): Adds support for session state.
  • UseResponseCompression(): Enables gzip compression for responses.

Conditional Middleware

Sometimes you may want to conditionally add middleware based on certain criteria. This can be done using UseWhen and MapWhen methods.

Example Using UseWhen:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"),
                builder =>
                {
                    builder.UseSimpleHeaderMiddleware();
                });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Middleware Registration

For custom middleware or classes implementing IMiddleware, register them in the ConfigureServices method to make them available in the DI container.

Example:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<SimpleHeaderMiddleware>();
    // ...
}

Conclusion

Configuring the middleware pipeline in ASP.NET Core is key to managing how HTTP requests and responses are processed. It involves creating middleware, registering necessary services, adding middleware in the right order in the Configure method of the Startup class, leveraging built-in middleware, and optionally using conditional middleware setups.

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement ASP.NET Core Middleware Pipeline Configuration

Step 1: Create an ASP.NET Core Web Application

First, let's create a new ASP.NET Core web application.

  1. Open Visual Studio (or use .NET CLI).
  2. Create a New Project.
    • Select ASP.NET Core Web App (Model-View-Controller) or any preferred template.
  3. Configure the Project:
    • Name your project (e.g., MiddlewareDemo).
    • Choose the framework version (.NET 6 or later is recommended).

Step 2: Understanding Middleware

Middleware is software that's assembled into an app pipeline to handle requests and responses. Each middleware component can:

  • Choose whether to pass the request to the next component in the pipeline.
  • Perform work before the next middleware component in the pipeline.
  • Perform work after the next middleware component responds.

Step 3: Configure Middleware in Startup.cs or Program.cs

In recent versions of ASP.NET Core (3.x and later), the configuration of middleware has moved to Program.cs. However, if you're using an older template, it might be in Startup.cs. Below, we will demonstrate using both approaches.

Using Program.cs (Recommended)

For .NET 6 and later:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/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();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

// Custom middleware
app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Middleware Start\n");
    await next(); // Passes control to the next middleware in the pipeline
    await context.Response.WriteAsync("Middleware End\n");
});

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Using Startup.cs

For .NET 5 and earlier:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/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();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        // Custom middleware
        app.Use(async (context, next) =>
        {
            await context.Response.WriteAsync("Middleware Start\n");
            await next(); // Passes control to the next middleware in the pipeline
            await context.Response.WriteAsync("Middleware End\n");
        });

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
              name: "default",
              pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

Step 4: Create Custom Middleware

We'll create a custom piece of middleware that sets a header on every HTTP response.

Define the Middleware

Create a file named ResponseHeaderMiddleware.cs:

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

public class ResponseHeaderMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        context.Response.Headers["X-Custom-Header"] = "MiddlewarePipeline";

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

Register the Middleware

Add the middleware to the pipeline. This should happen between UseRouting() and UseEndpoints():

In Program.cs:
var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseMiddleware<ResponseHeaderMiddleware>();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();
In Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...

    app.UseRouting();

    app.UseAuthorization();

    app.UseMiddleware<ResponseHeaderMiddleware>();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
          name: "default",
          pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}  

Step 5: Testing the Configuration

Run the application and navigate to the home page (e.g., https://localhost:PORT). Use browser developer tools (F12) or tools like Postman to inspect the HTTP response headers.

You should see an X-Custom-Header with the value MiddlewarePipeline.

Step 6: Adding Additional Built-In Middleware

The following middleware provides common functionalities such as handling errors, serving static files, routing, and authentication.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseMiddleware<ResponseHeaderMiddleware>(); 

    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    app.Run();
}

Step 7: Understanding the Order of Middleware

The order in which middleware components are added to the application pipeline is important because each middleware performs its operation in the order it was added.

  1. Authentication: Verifies user credentials.
  2. Authorization: Determines if a user has access to specific resources.
  3. Request Logging: Logs data about incoming requests.
  4. Static Files Service: Serves static files directly from the web root.
  5. Routing Middleware: Determines which route should handle the incoming request.
  6. Error Handling Middleware: Displays error pages when a request fails.
  7. Custom Middleware: Any middleware specific to your application.
  8. Endpoints Middleware: Maps the incoming request to endpoint handlers (MVC, Razor Pages, etc.).

Example: Adding Multiple Custom Middleware

Let's add two additional pieces of custom middleware:

Middleware 2: Log Request Method

Create a RequestMethodLoggerMiddleware.cs:

public class RequestMethodLoggerMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        var method = context.Request.Method;
        Console.WriteLine($"Request Method: {method}");
        await _next(context);
    }
}

Middleware 3: Set Response ContentType

Create a SetContentTypeMiddleware.cs:

public class SetContentTypeMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        await _next(context);
    }
}

Registering the Middleware

Modify the Configure method in Program.cs or Startup.cs:

app.UseRouting();

app.UseAuthorization();

app.UseMiddleware<RequestMethodLoggerMiddleware>();
app.UseMiddleware<ResponseHeaderMiddleware>();
app.UseMiddleware<SetContentTypeMiddleware>();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Step 8: Verify Middleware Order

Run the application and make requests to it. You should observe that the custom middleware RequestMethodLoggerMiddleware, ResponseHeaderMiddleware, and SetContentTypeMiddleware each perform their actions in the specified order.

  1. RequestMethodLoggerMiddleware logs the HTTP request method.
  2. ResponseHeaderMiddleware adds the custom header.
  3. SetContentTypeMiddleware sets the response content type to application/json.

Summary

By following these steps, you've learned how to configure the middleware pipeline in ASP.NET Core, including:

  • Adding built-in middleware components.
  • Creating and adding custom middleware components.
  • Understanding the importance of the middleware order.

Top 10 Interview Questions & Answers on ASP.NET Core Middleware Pipeline Configuration

Top 10 Questions and Answers on ASP.NET Core Middleware Pipeline Configuration

1. What is Middleware in ASP.NET Core?

  • Choose whether to pass the request to the next component in the pipeline.
  • Perform work before and after the next component in the pipeline is invoked.
  • Middleware are configured using the Use, Run, Map, or MapWhen methods in the Startup class.

2. How do you configure Middleware in ASP.NET Core?

Answer: Middleware is configured in the Configure method of the Startup class. You use various extension methods like Use, Run, and others to add middleware components to the pipeline. For example:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

3. What is the difference between Use, Run, Map, and MapWhen?

Answer:

  • Use: Adds a middleware delegate to the application's request pipeline. Use is used for middleware that should be called for each request.
  • Run: Adds a middleware delegate to the application’s request pipeline that can only short-circuit at the end of the pipeline because it doesn't call the next delegate.
  • Map and MapWhen: Used for branching the pipeline, creating sub-pipelines. Map branches the pipeline based on matching request paths. MapWhen branches the pipeline based on the outcome of a predicate.

4. Can you explain how ordering affects middleware execution in ASP.NET Core?

Answer: Yes, the order in which middleware handlers are added to the application pipeline is crucial because it defines the sequence in which they are invoked. For example, if you add app.UseAuthentication() followed by app.UseAuthorization(), the application first authenticates the user and then authorizes the request. Misordering can lead to security vulnerabilities.

5. How can you create a custom Middleware in ASP.NET Core?

Answer: Creating a custom middleware involves writing a class with an Invoke or InvokeAsync method that takes HttpContext and a RequestDelegate representing the next middleware in the pipeline. An example:

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        // Do something before invoking the next middleware
        await _next(context);
        // Do something after invoking the next middleware
    }
}

Register it in the Configure method:

app.UseMiddleware<CustomMiddleware>();

6. What is the purpose of the Terminal Middleware in ASP.NET Core?

Answer: Terminal middleware is a middleware that terminates the pipeline when it is invoked. It does not call the next middleware, effectively short-circuiting the pipeline. Common examples include middleware that returns a response like app.Run, ServeStaticFiles, and UseStatusCodePages.

7. Can you configure Middleware conditionally in ASP.NET Core?

Answer: Yes, you can configure middleware based on certain conditions like environment (development, production) or request paths. Use methods like Map and MapWhen to conditionally branch the pipeline. For example:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

app.MapWhen(context => context.Request.Path.StartsWithSegments("/special"), builder =>
{
    builder.UseMiddleware<SpecialMiddleware>();
});

8. What is the role of the RequestDelegate in ASP.NET Core Middleware?

Answer: The RequestDelegate is a function that corresponds to the middleware component that handles a request. It represents the rest of the pipeline (the next middleware in the pipeline) and can be called to execute the next component. When called, it may perform additional work before and after passing control to the next middleware.

9. How can you ensure that your Middleware only runs under specific conditions?

Answer: You can use the MapWhen method to branch the pipeline based on a predicate. For example, to ensure a middleware runs only for POST requests:

app.MapWhen(context => context.Request.Method == HttpMethods.Post, builder =>
{
    builder.UseMiddleware<CustomMiddleware>();
});

10. What is the difference between UseMiddleware and Use with a custom middleware class?

Answer: UseMiddleware is a specific method to add a middleware to the pipeline by specifying the middleware type and any required constructor arguments. Use is a more general method that can be used to add middleware using a delegate or an inline lambda, which provides more flexibility.

You May Like This Related .NET Topic

Login to post a comment.