Pregunta Cómo resolver el controlador de mensajes de la API web / Delegar comando desde el contenedor IoC en cada solicitud


Este artículo de MSDN describe cómo los manejadores de mensajes HTTP se pueden usar efectivamente en ASP.NET Web API para 'decorar' las solicitudes. Además, el artículo muestra el siguiente código para registrar sus manejadores personalizados en la interconexión de API web:

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

El problema que tengo con este enfoque es que esto registra el MessageHandler1 efectivamente como singleton. Esto está bien cuando el manejador no tiene estado ni dependencias, pero en un sistema que se basa en los principios de diseño SÓLIDO, es muy probable que esos manejadores tengan dependencias propias y es muy probable que algunas de esas dependencias necesiten un tiempo de vida que es más corto que singleton.

Si ese es el caso, dicho manejador de mensajes no debería crearse como singleton, ya que, en general, un componente nunca debería tener una vida útil que sea más larga que la duración de sus dependencias.

Entonces, mi pregunta es, ¿qué formas alternativas tenemos para registrar manejadores de mensajes personalizados, de tal manera que se puedan resolver desde nuestro contenedor IoC en cada solicitud?


12
2017-12-09 21:12


origen


Respuestas:


No conozco una API de registro diferente para los manejadores de mensajes, pero puede inyectar una fábrica en su manejador de mensajes para que resuelva las dependencias por llamada de método:

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

Es posible que desee omitir tener un efímero HttpMessageHandlery, en su lugar, solo solicite a la fábrica que cree una instancia del servicio que desee invocar en ese momento.

IHttpMessageHandlerFactory es una interfaz personalizada que acabo de inventar para la ocasión. Podría verse así:

public interface IHttpMessageHandlerFactory
{
    HttpMessageHandler Create();
}

5
2017-12-10 10:36



Tomando el ejemplo de Este artículo, Resolví el mismo problema de la siguiente manera:

Creé un servicio que alojará mi variabile global

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

Luego lo registré en mi contenedor IoC (en mi caso es SimpleInjector) utilizando por solicitud de por vida

container.RegisterWebApiRequest<IPerRequestScopedService, PerRequestScopedService>();

Luego usé el servicio dentro de DelegatingHandler de esta manera

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

Elegí esta implementación en lugar de Factory porque creo que está más orientada a la IoC, incluso si usa el patrón de Localizador de servicios en lugar de la Inyección de dependencia total.


3
2018-05-05 15:30