Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resolve Web API Message Handler / DelegatingHandler from IoC container on each request

This MSDN article describes how HTTP Message Handlers can effectively be used in ASP.NET Web API to 'decorate' requests. Furthermore, the article shows the following code to register your custom handlers into the Web API pipeline:

config.MessageHandlers.Add(new MessageHandler1());

The problem I have with this approach is that this registers the MessageHandler1 effectively as a singleton. This is fine when the handler itself has no state and no dependencies, but in a system that is based on the SOLID design principles, it's very likely that those handlers will have dependencies of their own and its very likely that some of those dependencies need a lifetime that is shorter than singleton.

If that's the case, such message handler should not be created as singleton, since in general, a component should never have a lifetime that is longer than the lifetime of its dependencies.

So my question is, what alternative ways do we have to register custom message handlers, in such way that they can be resolved from our IoC container on each request?

like image 268
Steven Avatar asked Dec 09 '13 21:12

Steven


2 Answers

I'm not aware of a different registration API for message handlers, but you can inject a Factory into your message handler in order to make it resolve dependencies per method call:

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);
    }
}

You might want to skip having an ephemeral HttpMessageHandler, and instead just ask the factory to create an instance of whatever service you wish to invoke at that point.

IHttpMessageHandlerFactory is a custom interface I just made up for the occasion. It could look like this:

public interface IHttpMessageHandlerFactory
{
    HttpMessageHandler Create();
}
like image 94
Mark Seemann Avatar answered Sep 24 '22 05:09

Mark Seemann


Taking cue from this article, I solved the same problem in the following way:

I created a service that will host my global 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);
    }
}

Then I registered it in my IoC container (in my case is SimpleInjector) using per request lifetime

container.RegisterWebApiRequest<IPerRequestScopedService, PerRequestScopedService>();

Then I used the service inside the DelegatingHandler in this way

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;
    }
}

I chose this implementation over the Factory because I think is more IoC oriented even if is using Service Locator pattern instead of full Dependency Injection.

like image 27
ilcorvo Avatar answered Sep 22 '22 05:09

ilcorvo