Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
После добавления сервисов в классе Startup они становятся доступны приложению, в том числе и в кастомных компонентах middleware. В middleware мы можем получить зависимости тремя способами:
Через конструктор
Через параметр метода Invoke/InvokeAsync
Через свойство HttpContext.RequestServices
При этом надо учитывать, что компоненты middleware создаются при создании класса Startup и живут в течение всего жизненного цикла приложения. То есть при последующих запросах asp.net core использует уже ранее созданный компонент. И это налагает ограничения на использование зависимостей в middleware.
В частности, если конструктор передается transient-сервис, который создается при каждом обращении к нему, то при последующих запросах мы будем использовать тот же самый сервис, так как конструктор middleware вызывается один раз - при создании приложения.
Например, определим сервис TimeService:
public class TimeService { public TimeService() { Time = System.DateTime.Now.ToString("hh:mm:ss"); } public string Time { get; } }
В конструкторе устанавливается свойство, которое хранит текущее время в виде строки.
Добавим новый компонент TimerMiddleware, который будет использовать этот сервис для вывода времени на веб-страницу:
using Microsoft.AspNetCore.Http; using System.Threading.Tasks; using DIApp.Services; namespace DIApp { public class TimerMiddleware { private readonly RequestDelegate _next; TimeService _timeService; public TimerMiddleware(RequestDelegate next, TimeService timeService) { _next = next; _timeService = timeService; } public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.Value.ToLower() == "/time") { context.Response.ContentType = "text/html; charset=utf-8"; await context.Response.WriteAsync($"Текущее время: {_timeService?.Time}"); } else { await _next.Invoke(context); } } } }
Если сервис TimeService добавляется в методе ConfigureServices()
, то мы сможем получить его через конструктор класса TimerMiddleware.
Логика компонента предполагает, что, если строка запроса равна строке "time", то с помощью TimeService возвращается текущее время. Иначе мы просто обращаемся к следующему делегату в конвейере обработки запроса.
Используем этот компонент в классе Startup:
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using DIApp.Services; using Microsoft.AspNetCore.Http; namespace DIApp { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<TimeService>(); } public void Configure(IApplicationBuilder app) { app.UseMiddleware<TimerMiddleware>(); app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } }
В итоге, если мы используем в адресной строке путь "time", то приложение выведет текущее время:
Однако сколько бы мы раз не обращались по этому пути, мы все время будем получать одно и то же время, так как объект TimerMiddleware был создан еще при первом запросе. Поэтому передача через конструктор middleware больше подходит для сервисов с жизненным циклом Singleton, которые создаются один раз для всех последующих запросов.
Если же в middleware необходимо использовать сервисы с жизненным циклом Scoped или Transient, то лучше их передавать через параметр метода Invoke/InvokeAsync:
public class TimerMiddleware { private readonly RequestDelegate _next; public TimerMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, TimeService timeService) { if (context.Request.Path.Value.ToLower() == "/time") { context.Response.ContentType = "text/html; charset=utf-8"; await context.Response.WriteAsync($"Текущее время: {timeService?.Time}"); } else { await _next.Invoke(context); } } }
Также можно получать сервисы через свойство HttpContext.RequestServices в тех ситуациях, когда необходимость использования сервисов может зависеть от среды выполнения:
using DIApp.Services; using Microsoft.AspNetCore.Http; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace DIApp { public class TimerMiddleware { private readonly RequestDelegate _next; public TimerMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { context.Response.ContentType = "text/html; charset=utf-8"; if (context.Request.Path.Value.ToLower() == "/time") { TimeService timeService = context.RequestServices.GetService<TimeService>(); await context.Response.WriteAsync($"Текущее время: {timeService?.Time}"); } else { await context.Response.WriteAsync($"Параметр неопределен"); } } } }