Fundamentals - Middleware - Middleware overview
In this article
-
Chooses whether to pass the request to the
next
component in the pipeline. -
Can perform work before and after the
next
component in the pipeline.
Middleware code analysis
WebApplication
Create a middleware pipeline with var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Short-circuiting the request pipeline
Warning Don't call
next.Invoke
during or after the response has been sent to the client. After an HttpResponse has started, changes result in an exception. For example, setting headers and a status code throw an exception after the response starts. Writing to the response body after callingnext
:
May cause a protocol violation, such as writing more than the stated Content-Length
.
May corrupt the body format, such as writing an HTML footer to a CSS file.
HasStarted is a useful hint to indicate if headers have been sent or the body has been written to.
-
May cause a protocol violation, such as writing more than the stated
Content-Length
. -
May corrupt the body format, such as writing an HTML footer to a CSS file.
Run
delegates
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
next
Prefer app.Use overload that requires passing the context to -
Requires passing the context to
next
. -
Saves two internal per-request allocations that are required when using the other overload.
Middleware order
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
-
Middleware that is not added when creating a new web app with individual users accounts is commented out.
-
Not every middleware appears in this exact order, but many do. For example:
-
UseCors
,UseAuthentication
, andUseAuthorization
must appear in the order shown. -
UseCors
currently must appear beforeUseResponseCaching
. This requirement is explained in GitHub issue dotnet/aspnetcore #23218. -
UseRequestLocalization
must appear before any middleware that might check the request culture, for example,app.UseStaticFiles()
. -
UseRateLimiter
must be called afterUseRouting
when rate limiting endpoint specific APIs are used. For example, if the[EnableRateLimiting]
attribute is used,UseRateLimiter
must be called afterUseRouting
. When calling only global limiters,UseRateLimiter
can be called beforeUseRouting
.
-
app.UseResponseCaching();
app.UseResponseCompression();
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
-
Exception/error handling
-
When the app runs in the Development environment:
-
Developer Exception Page Middleware (UseDeveloperExceptionPage) reports app runtime errors.
-
Database Error Page Middleware (UseDatabaseErrorPage) reports database runtime errors.
-
-
When the app runs in the Production environment:
-
Exception Handler Middleware (UseExceptionHandler) catches exceptions thrown in the following middlewares.
-
HTTP Strict Transport Security Protocol (HSTS) Middleware (UseHsts) adds the
Strict-Transport-Security
header.
-
-
-
HTTPS Redirection Middleware (UseHttpsRedirection) redirects HTTP requests to HTTPS.
-
Static File Middleware (UseStaticFiles) returns static files and short-circuits further request processing.
-
Cookie Policy Middleware (UseCookiePolicy) conforms the app to the EU General Data Protection Regulation (GDPR) regulations.
-
Routing Middleware (UseRouting) to route requests.
-
Authentication Middleware (UseAuthentication) attempts to authenticate the user before they're allowed access to
secure
resources. -
Authorization Middleware (UseAuthorization) authorizes a user to access
secure
resources. -
Session Middleware (UseSession) establishes and maintains session state. If the app uses session state, call Session Middleware after Cookie Policy Middleware and before MVC Middleware.
-
Endpoint Routing Middleware (UseEndpoints with MapRazorPages) to add Razor Pages endpoints to the request pipeline.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.MapRazorPages();
UseCors
and UseStaticFiles
order
Forwarded Headers Middleware order
Branch the middleware pipeline
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
Request | Response |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
Request | Response |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
Built-in middleware
Middleware | Description | Order |
---|---|---|
Authentication | Provides authentication support. | Before HttpContext.User is needed. Terminal for OAuth callbacks. |
Authorization | Provides authorization support. | Immediately after the Authentication Middleware. |
Cookie Policy | Tracks consent from users for storing personal information and enforces minimum standards for cookie fields, such as secure and SameSite . |
Before middleware that issues cookies. Examples: Authentication, Session, MVC (TempData). |
CORS | Configures Cross-Origin Resource Sharing. | Before components that use CORS. UseCors currently must go before UseResponseCaching due to this bug. |
DeveloperExceptionPage | Generates a page with error information that is intended for use only in the Development environment. | Before components that generate errors. The project templates automatically register this middleware as the first middleware in the pipeline when the environment is Development. |
Diagnostics | Several separate middlewares that provide a developer exception page, exception handling, status code pages, and the default web page for new apps. | Before components that generate errors. Terminal for exceptions or serving the default web page for new apps. |
Forwarded Headers | Forwards proxied headers onto the current request. | Before components that consume the updated fields. Examples: scheme, host, client IP, method. |
Health Check | Checks the health of an ASP.NET Core app and its dependencies, such as checking database availability. | Terminal if a request matches a health check endpoint. |
Header Propagation | Propagates HTTP headers from the incoming request to the outgoing HTTP Client requests. | |
HTTP Logging | Logs HTTP Requests and Responses. | At the beginning of the middleware pipeline. |
HTTP Method Override | Allows an incoming POST request to override the method. | Before components that consume the updated method. |
HTTPS Redirection | Redirect all HTTP requests to HTTPS. | Before components that consume the URL. |
HTTP Strict Transport Security (HSTS) | Security enhancement middleware that adds a special response header. | Before responses are sent and after components that modify requests. Examples: Forwarded Headers, URL Rewriting. |
MVC | Processes requests with MVC/Razor Pages. | Terminal if a request matches a route. |
OWIN | Interop with OWIN-based apps, servers, and middleware. | Terminal if the OWIN Middleware fully processes the request. |
Output Caching | Provides support for caching responses based on configuration. | Before components that require caching. UseRouting must come before UseOutputCaching . UseCORS must come before UseOutputCaching . |
Response Caching | Provides support for caching responses. This requires client participation to work. ```Use``` output caching for complete server control. | Before components that require caching. UseCORS must come before UseResponseCaching . Is typically not beneficial for UI apps such as Razor Pages because browsers generally set request headers that prevent caching. Output caching benefits UI apps. |
Request Decompression | Provides support for decompressing requests. | Before components that read the request body. |
Response Compression | Provides support for compressing responses. | Before components that require compression. |
Request Localization | Provides localization support. | Before localization sensitive components. Must appear after Routing Middleware when using RouteDataRequestCultureProvider. |
Request Timeouts | Provides support for configuring request timeouts, global and per endpoint. | UseRequestTimeouts must come after UseExceptionHandler , UseDeveloperExceptionPage , and UseRouting . |
Endpoint Routing | Defines and constrains request routes. | Terminal for matching routes. |
SPA | Handles all requests from this point in the middleware chain by returning the default page for the Single Page Application (SPA) | Late in the chain, so that other middleware for serving static files, MVC actions, etc., takes precedence. |
Session | Provides support for managing user sessions. | Before components that require Session. |
Static Files | Provides support for serving static files and directory browsing. | Terminal if a request matches a file. |
URL Rewrite | Provides support for rewriting URLs and redirecting requests. | Before components that consume the URL. |
W3CLogging | Generates server access logs in the W3C Extended Log File Format. | At the beginning of the middleware pipeline. |
WebSockets | Enables the WebSockets protocol. | Before components that are required to accept WebSocket requests. |
Additional resources
-
Lifetime and registration options contains a complete sample of middleware with scoped, transient, and singleton lifetime services.
-
Write custom ASP.NET Core middleware
-
Test ASP.NET Core middleware
-
Configure gRPC-Web in ASP.NET Core
-
Migrate HTTP handlers and modules to ASP.NET Core middleware
-
App startup in ASP.NET Core
-
Request Features in ASP.NET Core
-
Factory-based middleware activation in ASP.NET Core
-
Middleware activation with a third-party container in ASP.NET Core