10 KiB
title | author | description | monikerRange | ms.author | ms.custom | ms.date | uid |
---|---|---|---|---|---|---|---|
Write custom ASP.NET Core middleware | rick-anderson | Learn how to write custom ASP.NET Core middleware. | >= aspnetcore-3.1 | riande | mvc | 12/18/2021 | fundamentals/middleware/write |
Write custom ASP.NET Core middleware
:::moniker range=">= aspnetcore-6.0"
By Fiyaz Hasan, Rick Anderson, and Steve Smith
Middleware is software that's assembled into an app pipeline to handle requests and responses. ASP.NET Core provides a rich set of built-in middleware components, but in some scenarios you might want to write a custom middleware.
This topic describes how to write convention-based middleware. For an approach that uses strong typing and per-request activation, see xref:fundamentals/middleware/extensibility.
Middleware class
Middleware is generally encapsulated in a class and exposed with an extension method. Consider the following inline middleware, which sets the culture for the current request from a query string:
:::code language="csharp" source="~/fundamentals/middleware/write/6sample/WebMiddleware/Program.cs" id="snippet_first" highlight="8-21":::
The preceding highlighted inline middleware is used to demonstrate creating a middleware component by calling xref:Microsoft.AspNetCore.Builder.UseExtensions.Use%2A?displayProperty=fullName. The preceding Use
extension method adds a middleware delegate defined in-line to the application's request pipeline.
There are two overloads available for the Use
extension:
- One takes a xref:Microsoft.AspNetCore.Http.HttpContext and a
Func<Task>
. Invoke theFunc<Task>
without any parameters. - The other takes a
HttpContext
and a xref:Microsoft.AspNetCore.Http.RequestDelegate. Invoke theRequestDelegate
by passing theHttpContext
.
Prefer using the later overload as it saves two internal per-request allocations that are required when using the other overload.
Test the middleware by passing in the culture. For example, request https://localhost:5001/?culture=es-es
.
For ASP.NET Core's built-in localization support, see xref:fundamentals/localization.
The following code moves the middleware delegate to a class:
:::code language="csharp" source="~/fundamentals/middleware/write/6sample/WebMiddleware/RequestCultureMiddleware.cs" id="snippet_1":::
The middleware class must include:
- A public constructor with a parameter of type xref:Microsoft.AspNetCore.Http.RequestDelegate.
- A public method named
Invoke
orInvokeAsync
. This method must:- Return a
Task
. - Accept a first parameter of type xref:Microsoft.AspNetCore.Http.HttpContext.
- Return a
Additional parameters for the constructor and Invoke
/InvokeAsync
are populated by dependency injection (DI).
Typically, an extension method is created to expose the middleware through xref:Microsoft.AspNetCore.Builder.IApplicationBuilder:
:::code language="csharp" source="~/fundamentals/middleware/write/6sample/WebMiddleware/RequestCultureMiddleware.cs" id="snippet_all" highlight="30-99":::
The following code calls the middleware from Program.cs
:
:::code language="csharp" source="~/fundamentals/middleware/write/6sample/WebMiddleware/Program.cs" id="snippet_2" highlight="9":::
Middleware dependencies
Middleware should follow the Explicit Dependencies Principle by exposing its dependencies in its constructor. Middleware is constructed once per application lifetime.
Middleware components can resolve their dependencies from dependency injection (DI) through constructor parameters. xref:Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.UseMiddleware%2A can also accept additional parameters directly.
Per-request middleware dependencies
Middleware is constructed at app startup and therefore has application life
time. Scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. To share a scoped service between middleware and other types, add these services to the InvokeAsync
method's signature. The InvokeAsync
method can accept additional parameters that are populated by DI:
:::code language="csharp" source="~/fundamentals/middleware/write/6sample/WebMiddleware/MyCustomMiddleware.cs":::
Lifetime and registration options contains a complete sample of middleware with scoped lifetime services.
The following code is used to test the preceding middleware:
:::code language="csharp" source="~/fundamentals/middleware/write/6sample/WebMiddleware/Program.cs" id="snippet_3" highlight="4,10":::
The IMessageWriter
interface and implementation:
:::code language="csharp" source="~/fundamentals/middleware/write/6sample/WebMiddleware/IMessageWriter.cs":::
Additional resources
- Sample code used in this article
- UseExtensions source on GitHub
- Lifetime and registration options contains a complete sample of middleware with scoped, transient, and singleton lifetime services.
- DEEP DIVE: HOW IS THE ASP.NET CORE MIDDLEWARE PIPELINE BUILT
- xref:fundamentals/middleware/index
- xref:test/middleware
- xref:migration/http-modules
- xref:fundamentals/startup
- xref:fundamentals/request-features
- xref:fundamentals/middleware/extensibility
- xref:fundamentals/middleware/extensibility-third-party-container
:::moniker-end
:::moniker range="< aspnetcore-6.0"
By Rick Anderson and Steve Smith
Middleware is software that's assembled into an app pipeline to handle requests and responses. ASP.NET Core provides a rich set of built-in middleware components, but in some scenarios you might want to write a custom middleware.
[!NOTE] This topic describes how to write convention-based middleware. For an approach that uses strong typing and per-request activation, see xref:fundamentals/middleware/extensibility.
Middleware class
Middleware is generally encapsulated in a class and exposed with an extension method. Consider the following middleware, which sets the culture for the current request from a query string:
:::code language="csharp" source="write/snapshot/StartupCulture.cs":::
The preceding sample code is used to demonstrate creating a middleware component. For ASP.NET Core's built-in localization support, see xref:fundamentals/localization.
Test the middleware by passing in the culture. For example, request https://localhost:5001/?culture=no
.
The following code moves the middleware delegate to a class:
:::code language="csharp" source="write/snapshot/RequestCultureMiddleware.cs":::
The middleware class must include:
- A public constructor with a parameter of type xref:Microsoft.AspNetCore.Http.RequestDelegate.
- A public method named
Invoke
orInvokeAsync
. This method must:- Return a
Task
. - Accept a first parameter of type xref:Microsoft.AspNetCore.Http.HttpContext.
- Return a
Additional parameters for the constructor and Invoke
/InvokeAsync
are populated by dependency injection (DI).
Middleware dependencies
Middleware should follow the Explicit Dependencies Principle by exposing its dependencies in its constructor. Middleware is constructed once per application lifetime. See the Per-request middleware dependencies section if you need to share services with middleware within a request.
Middleware components can resolve their dependencies from dependency injection (DI) through constructor parameters. xref:Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.UseMiddleware%2A can also accept additional parameters directly.
Per-request middleware dependencies
Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the InvokeAsync
method's signature. The InvokeAsync
method can accept additional parameters that are populated by DI:
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
// IMyScopedService is injected into InvokeAsync
public async Task InvokeAsync(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty = 1000;
await _next(httpContext);
}
}
Lifetime and registration options contains a complete sample of middleware with scoped lifetime services.
Middleware extension method
The following extension method exposes the middleware through xref:Microsoft.AspNetCore.Builder.IApplicationBuilder:
:::code language="csharp" source="write/snapshot/RequestCultureMiddlewareExtensions.cs":::
The following code calls the middleware from Startup.Configure
:
:::code language="csharp" source="write/snapshot/Startup.cs" highlight="5":::
Additional resources
- Lifetime and registration options contains a complete sample of middleware with scoped, transient, and singleton lifetime services.
- xref:fundamentals/middleware/index
- xref:test/middleware
- xref:migration/http-modules
- xref:fundamentals/startup
- xref:fundamentals/request-features
- xref:fundamentals/middleware/extensibility
- xref:fundamentals/middleware/extensibility-third-party-container
:::moniker-end