Как разрешить обработчик сообщений API Web/DelegatingHandler из контейнера IoC по каждому запросу
В этой статье MSDN описывается, как обработчики HTTP-сообщений могут эффективно использоваться в ASP.NET Web API для "украшения" запросов. Кроме того, в статье показан следующий код для регистрации ваших пользовательских обработчиков в конвейере веб-API:
config.MessageHandlers.Add(new MessageHandler1());
Проблема с этим подходом заключается в том, что он эффективно регистрирует MessageHandler1
как одноэлементный. Это нормально, когда сам обработчик не имеет состояния и никаких зависимостей, но в системе, основанной на принципах дизайна SOLID, очень вероятно, что эти обработчики будут иметь собственные зависимости и очень вероятно, что некоторые из этих зависимостей нуждаются в время жизни, которое короче, чем одиночный.
Если в этом случае такой обработчик сообщения не должен быть создан как singleton, так как в общем случае компонент никогда не должен иметь продолжительность жизни, которая больше, чем время жизни его зависимостей.
Итак, мой вопрос в том, какие альтернативные способы мы должны регистрировать обработчиков пользовательских сообщений таким образом, чтобы их можно было решить из нашего контейнера IoC по каждому запросу?
Ответы
Ответ 1
Мне не известно о другом API регистрации для обработчиков сообщений, но вы можете ввести Factory в свой обработчик сообщений, чтобы заставить его разрешать зависимости для вызова метода:
public class LifetimeProxyMessageHandler : DelegatingHandler
{
private readonly IHttpMessageHandlerFactory factory;
public LifetimeProxyMessageHandler(IHttpMessageHandlerFactory factory)
{
if (factory == null)
throw new ArgumentNullException("factory");
this.factory = factory;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpMessageHandler ephemeralHandler = factory.Create();
ephemeralHandler.InnerHandler = this.InnerHandler;
var invoker = new HttpMessageInvoker(ephemeralHandler);
return invoker.SendAsync(request, cancellationToken);
}
}
Возможно, вы захотите пропустить эфемерный HttpMessageHandler
, а вместо этого попросите Factory создать экземпляр любой службы, которую вы хотите вызвать в этой точке.
IHttpMessageHandlerFactory
- это настраиваемый интерфейс, который я только что придумал. Это может выглядеть так:
public interface IHttpMessageHandlerFactory
{
HttpMessageHandler Create();
}
Ответ 2
Взяв cue из в этой статье, я решил ту же проблему следующим образом:
Я создал службу, в которой будет размещен мой глобальный variabile
public interface IPerRequestScopedService : IDisposable
{
string ClientId { get; set; }
}
public class PerRequestScopedService : IPerRequestScopedService
{
public string ClientId { get; set; }
public void Dispose()
{
Trace.TraceInformation("Object {0} disposed", this.GetType().Name);
}
}
Затем я зарегистрировал его в своем контейнере IoC (в моем случае SimpleInjector), используя время выполнения запроса
container.RegisterWebApiRequest<IPerRequestScopedService, PerRequestScopedService>();
Тогда я использовал службу внутри DelegatingHandler таким образом
public class HeaderReaderHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
Trace.TraceInformation("start HeaderReaderHandler");
IEnumerable<string> headerValues;
if (request.Headers.TryGetValues("x-client-id", out headerValues))
{
string token = headerValues.First();
var requestScopedService = request.GetDependencyScope().GetService(typeof(IPerRequestScopedService)) as IPerRequestScopedService;
requestScopedService.ClientId = token;
}
// Call the inner handler.
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Я выбрал эту реализацию по сравнению с Factory, потому что, я думаю, более ориентирован на IoC, даже если использует шаблон Service Locator вместо полной инъекции зависимостей.