AspNetCore.Docs/aspnetcore/fundamentals/middleware/write.md

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:

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:

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

:::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:

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

:::moniker-end