Correct usage of IHttpMessageHandlerFactory? - c#

I have a use case for HttpMessageHandler that requires new options on instantiation. I believe that IHttpMessageHandlerFactory.CreateHandler is the correct API here, but I'm not sure if this usage is correct:
public class MyDelegatingHandler : DelegatingHandler {
public MyDelegatingHandler(
HttpMessageHandler internalHandler
)
{
_internalHandler = internalHandler;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken
)
{
// custom logic
return await _internalHandler.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
private HttpMessageHandler _internalHandler;
}
public class MyHttpClientFactory {
public MyHttpClientFactory(
IHttpMessageHandlerFactory factory
)
{
_factory = factory;
}
public HttpClient CreateClient(
// arguments
)
{
return new HttpClient(
new MyDelegatingHandler(_factory.CreateHandler()),
disposeHandler: false
);
}
private IHttpMessageHandlerFactory _factory;
}
Assuming MyDelegatingHandler has no state that needs to be disposed on its own, will this correctly use the lifetime management features of HttpClientFactory injected through the IHttpMessageHandlerFactory dependency?

Related

Is it possible to inject IHttpClientFactory to a strongly typed client?

As the heading tells.
Let's say I register a strongly typed client like
var services = new ServiceCollection();
//A named client is another option that could be tried since MSDN documentation shows that being used when IHttpClientFactory is injected.
//However, it appears it gives the same exception.
//services.AddHttpClient("test", httpClient =>
services.AddHttpClient<TestClient>(httpClient =>
{
httpClient.BaseAddress = new Uri("");
});
.AddHttpMessageHandler(_ => new TestMessageHandler());
//Registering IHttpClientFactory isn't needed, hence commented.
//services.AddSingleton(sp => sp.GetRequiredService<IHttpClientFactory>());
var servicesProvider = services.BuildServiceProvider(validateScopes: true);
public class TestClient
{
private IHttpClientFactory ClientFactory { get; }
public TestClient(IHttpClientFactory clientFactory)
{
ClientFactory = clientFactory;
}
public async Task<HttpResponseMessage> CallAsync(CancellationToken cancellation = default)
{
//using(var client = ClientFactory.CreateClient("test"))
using(var client = ClientFactory.CreateClient())
{
return await client.GetAsync("/", cancellation);
}
}
}
// This throws with "Message: System.InvalidOperationException : A suitable constructor
// for type 'Test.TestClient' could not be located. Ensure the type is concrete and services
// are registered for all parameters of a public constructor.
var client = servicesProvider.GetService<TestClient>();
But as noted in the comments, an exception will be thrown. Do I miss something bovious or is this sort of an arrangement not possible?
<edit: If IHttpClientFactory is registered, a NullReferenceException is thrown while trying to resolve client. Strange, strange.
<edit 2: The scenario I'm thinking to avoid is described and discussed also at https://github.com/aspnet/Extensions/issues/924 and maybe the way written there is one, perhaps not as satisfactory, way of avoiding some problems.
This happens in a XUnit project, probably doesn't have anything to do with the problem, but who knows. :)
<edit 3: A console program to show the problem.
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace TypedClientTest
{
public class TestClient
{
private IHttpClientFactory ClientFactory { get; }
public TestClient(IHttpClientFactory clientFactory)
{
ClientFactory = clientFactory;
}
public async Task<HttpResponseMessage> TestAsync(CancellationToken cancellation = default)
{
using (var client = ClientFactory.CreateClient())
{
return await client.GetAsync("/", cancellation);
}
}
}
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services
.AddHttpClient<TestClient>(httpClient => httpClient.BaseAddress = new Uri("https://www.github.com/"));
var servicesProvider = services.BuildServiceProvider(validateScopes: true);
//This throws that there is not a suitable constructor. Should it?
var client = servicesProvider.GetService<TestClient>();
}
}
}
with install-package Microsoft.Extensions.Http and install-package Microsoft.Extensions.DependencyInjection.
Also in the exception stack there reads
at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func1 valueFactory)
at Microsoft.Extensions.Http.DefaultTypedHttpClientFactory1.Cache.get_Activator()
at ** Microsoft.Extensions.Http.DefaultTypedHttpClientFactory1.CreateClient(HttpClient httpClient) **
at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func1 valueFactory)
at Microsoft.Extensions.Http.DefaultTypedHttpClientFactory1.Cache.get_Activator()
at Microsoft.Extensions.Http.DefaultTypedHttpClientFactory1.CreateClient(HttpClient httpClient)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitTransient(TransientCallSite transientCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at TypedClientTest.Program.Main(String[] args) in C:\projektit\testit\TypedClientTest\TypedClientTest\Program.cs:line 40
which of course points towards the problem. But needs probably further debugging.
<edit 4: So going to the source, the problem is visible at https://github.com/aspnet/Extensions/blob/557995ec322f1175d6d8a72a41713eec2d194871/src/HttpClientFactory/Http/src/DefaultTypedHttpClientFactory.cs#L47 and in https://github.com/aspnet/Extensions/blob/11cf90103841c35cbefe9afb8e5bf9fee696dd17/src/HttpClientFactory/Http/src/DependencyInjection/HttpClientFactoryServiceCollectionExtensions.cs in general.
There are probably some ways to go about this now. :)
<edit 5:
So it appears calling .AddHttpClient for a typed client that has IHttpClientFactory one ends up in a "weird place". And indeed, it's not possible to use IHttpClientFactory to create a typed client of its own type.
One way of making this with named client could be something like
public static class CustomServicesCollectionExtensions
{
public static IHttpClientBuilder AddTypedHttpClient<TClient>(this IServiceCollection serviceCollection, Action<HttpClient> configureClient) where TClient: class
{
//return serviceCollection.Add(new ServiceDescriptor(typeof(TClient).Name, f => new ...,*/ ServiceLifetime.Singleton));
servicesCollection.AddTransient<TClient>();
return serviceCollection.AddHttpClient(typeof(TType).Name, configureClient);
}
}
public static class HttpClientFactoryExtensions
{
public static HttpClient CreateClient<TClient>(this IHttpClientFactory clientFactory)
{
return clientFactory.CreateClient(typeof(TClient).Name);
}
}
public class TestClient
{
private IHttpClientFactory ClientFactory { get; }
public TestClient(IHttpClientFactory clientFactory)
{
ClientFactory = clientFactory;
}
public async Task<HttpResponseMessage> Test(CancellationToken cancellation = default)
{
using(var client = ClientFactory.CreateClient<TestClient>())
{
return await client.GetAsync("/", cancellation);
}
}
}
Which mimicks what the extension methods already do. It's of course now possible to expose the lifetime services better too.
Please read about Typed clients:
A typed client accepts a HttpClient parameter in its constructor
Instead of IHttpClientFactory your class should accept an HttpClient in its constructor, which will be provided by DI (enabled with the AddHttpClient extension).
public class TestClient
{
private HttpClient Client { get; }
public TestClient(HttpClient client)
{
Client = client;
}
public Task<HttpResponseMessage> CallAsync(CancellationToken cancellation = default)
{
return client.GetAsync("/", cancellation);
}
}
Edit
(based on above edits)
If you want to override the default behavior of the AddHttpClient extension method, then you should register your implementation directly:
var services = new ServiceCollection();
services.AddHttpClient("test", httpClient =>
{
httpClient.BaseAddress = new Uri("https://localhost");
});
services.AddScoped<TestClient>();
var servicesProvider = services.BuildServiceProvider(validateScopes: true);
using (var scope = servicesProvider.CreateScope())
{
var client = scope.ServiceProvider.GetRequiredService<TestClient>();
}
public class TestClient
{
private IHttpClientFactory ClientFactory { get; }
public TestClient(IHttpClientFactory clientFactory)
{
ClientFactory = clientFactory;
}
public Task<HttpResponseMessage> CallAsync(CancellationToken cancellation = default)
{
using (var client = ClientFactory.CreateClient("test"))
{
return client.GetAsync("/", cancellation);
}
}
}

Unit Testing methods within which extension methods are used for external resources

I need to Unit test RequestToken method that uses HttpClient and extension method RequestPasswordTokenAsync.
I have got a null reference exception even when a delegatingHandler is passed to the HttpClient.
var delegatingHandler = new DelegatingHandlerStub(false);
var httpClient = new HttpClient(delegatingHandler);
var tokenServices = new TokenServices(httpClient)
tokenServices.RequestToken(passwordTokenRequest); //exception
public class TokenServices : ITokenServices
{
private readonly HttpClient _httpClient;
public TokenServices(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<TokenResponse> RequestToken(PasswordTokenRequest request)
{
var response = await _httpClient.RequestPasswordTokenAsync(request);
}
}
public class DelegatingHandlerStub : DelegatingHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public DelegatingHandlerStub(bool toThrowException)
{
_handlerFunc = (request, cancellationToken) =>
{
if (toThrowException)
throw new Exception();
return Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
};
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
}
namespace IdentityModel.Client
{
public static class HttpClientTokenRequestExtensions
{
public static Task<TokenResponse> RequestPasswordTokenAsync(this HttpMessageInvoker client, PasswordTokenRequest request, CancellationToken cancellationToken = default(CancellationToken));
}
}
I have to write a wrapper around HttpClient.
If there are a better solution, please post it here

Unit testing Custom MessageHandler

I have a custom handler as below:
public class LoggingHandler : DelegatingHandler
{
public LoggingHandler()
{
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var logger = new Logger(new something1(), param2, param3);
logger.LogInformation(
$"Incoming request: {request.Method} {request.RequestUri} );
.
.
.
.
return httpResponse;
}
}
I am familiar with Moq and I am able to moq the request and response message and assert successfully on that.
However as you can see I have a logger initialization done in the SendAsync method and log information regarding request, response and errors.
How can I test the logger in this workflow?.
Problem will be that because of the manual initialization of the logger it is difficult to mock it.
The logger should have been an injected dependency.
public class LoggingHandler : DelegatingHandler {
private readonly ILogger logger;
public LoggingHandler(ILogger logger) {
this.logger = logger;
}
//...
If injection is not an option then have a virtual factory method that can be overridden when testing.
public class LoggingHandler : DelegatingHandler {
public LoggingHandler() {
}
protected virtual ILogger CreateLogger() {
//...
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken) {
var logger = CreateLogger();
logger.LogInformation(
$"Incoming request: {request.Method} {request.RequestUri} );
//....
return httpResponse;
}
//...

CA1001 Types that own disposable fields should be disposable over HttpClient

I have a HttpClientWrapper class to bring unit testability over my classes which are making HttpCalls.
This class is injected via IoC, and acts as a singleton. Because I would like to use same HttpClient instance over and over again without disposing it.
Do HttpClient and HttpClientHandler have to be disposed?
public class HttpClientWrapper : IHttpClientWrapper
{
readonly HttpClient _client;
public HttpClientWrapper()
{
_client = new HttpClient();
}
public Uri BaseAddress
{
get { return _client.BaseAddress; }
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage message)
{
Task<HttpResponseMessage> sendAsync = _client.SendAsync(message);
return sendAsync;
}
}
Following clears the warning naturally.
public class HttpClientWrapper : IHttpClientWrapper, IDisposable
{
readonly HttpClient _client;
public HttpClientWrapper()
{
_client = new HttpClient();
}
public Uri BaseAddress
{
get { return _client.BaseAddress; }
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage message)
{
Task<HttpResponseMessage> sendAsync = _client.SendAsync(message);
return sendAsync;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_client != null)
{
_client.Dispose();
}
}
}
When I run code analysis, I am receiving following warning from IDE.
CA1001 Types that own disposable fields should be disposable Implement
IDisposable on 'HttpClientWrapper' because it creates members of the
following IDisposable types: 'HttpClient'. If 'HttpClientWrapper' has
previously shipped, adding new members that implement IDisposable to
this type is considered a breaking change to existing consumers.
So am I missing something here? Since I would like to keep only one instance of HttpClient and would like not to dispose it, can I safely surpress the warning?
Does second version of class implementation makes sense?

Decorating ASP.NET Web API IHttpController

I'm trying to wrap Web API controllers (IHttpController implementations) with decorators, but when I do this, Web API throws an exception, because somehow it is expecting the actual implementation.
Applying decorators to controllers is a trick I successfully apply to MVC controllers and I obviously like to do the same in Web API.
I created a custom IHttpControllerActivator that allows resolving decorated IHttpController implementations. Here's a stripped implementation:
public class CrossCuttingConcernHttpControllerActivator : IHttpControllerActivator {
private readonly Container container;
public CrossCuttingConcernHttpControllerActivator(Container container) {
this.container = container;
}
public IHttpController Create(HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = (IHttpController)this.container.GetInstance(controllerType);
// Wrap the instance in one or multiple decorators. Note that in reality, the
// decorator is applied by the container, but that doesn't really matter here.
return new MyHttpControllerDecorator(controller);
}
}
My decorator looks like this:
public class MyHttpControllerDecorator : IHttpController {
private readonly IHttpController decoratee;
public MyHttpControllerDecorator(IHttpController decoratee) {
this.decoratee = decoratee;
}
public Task<HttpResponseMessage> ExecuteAsync(
HttpControllerContext controllerContext,
CancellationToken cancellationToken)
{
// this decorator does not add any logic. Just the minimal amount of code to
// reproduce the issue.
return this.decoratee.ExecuteAsync(controllerContext, cancellationToken);
}
}
However, when I run my application and request the ValuesController, Web API throws me the following InvalidCastException:
Unable to cast object of type 'WebApiTest.MyHttpControllerDecorator'
to type 'WebApiTest.Controllers.ValuesController'.
Stacktrace:
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
It's just as if Web API supplies us with the IHttpController abstraction but skips it and still depends on the implementation itself. This would of course be a severe violation of the Dependency Inversion principle and make the abstraction utterly useless. So I'm probably doing something wrong instead.
What I'm I doing wrong? How can I happily decorate my API Controllers?
I would say, that the natural, designed way how to achieve this behaviour in ASP.NET Web API is with the Custom Message Handlers / Delegation Handlers
For example I do have this DelegationHandler in place
public class AuthenticationDelegationHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage>
SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// I. do some stuff to create Custom Principal
// e.g.
var principal = CreatePrincipal();
...
// II. return execution to the framework
return base.SendAsync(request, cancellationToken).ContinueWith(t =>
{
HttpResponseMessage resp = t.Result;
// III. do some stuff once finished
// e.g.:
// SetHeaders(resp, principal);
return resp;
});
}
And this is how to inject that into the structure:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new AuthenticationDelegationHandler());
You can work around this by implementing IHttpActionInvoker and "converting" the decorator into the decorated instance at the point that the IHttpController abstraction is no longer relevant.
This is easily done by inheriting from ApiControllerActionInvoker.
(I've hard coded the example and would expect any real world implementation to be more flexible.)
public class ContainerActionInvoker : ApiControllerActionInvoker
{
private readonly Container container;
public ContainerActionInvoker(Container container)
{
this.container = container;
}
public override Task<HttpResponseMessage> InvokeActionAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken)
{
if (actionContext.ControllerContext.Controller is MyHttpControllerDecorator)
{
MyHttpControllerDecorator decorator =
(MyHttpControllerDecorator)actionContext.ControllerContext.Controller;
// decoratee changed to public for the example
actionContext.ControllerContext.Controller = decorator.decoratee;
}
var result = base.InvokeActionAsync(actionContext, cancellationToken);
return result;
}
}
This was registered in Global.asax.cs
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new CrossCuttingConcernHttpControllerActivator(container));
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpActionInvoker),
new ContainerActionInvoker(container));
Whether you'd actually want to do this is another matter - who knows the ramifications of altering actionContext?
You can provide a custom implementation of IHttpControllerSelector to alter the type instantiated for a particular controller. (Please note I have not tested this to exhaustion)
Update the decorator to be generic
public class MyHttpControllerDecorator<T> : MyHttpController
where T : MyHttpController
{
public readonly T decoratee;
public MyHttpControllerDecorator(T decoratee)
{
this.decoratee = decoratee;
}
public Task<HttpResponseMessage> ExecuteAsync(
HttpControllerContext controllerContext,
CancellationToken cancellationToken)
{
return this.decoratee.ExecuteAsync(controllerContext, cancellationToken);
}
[ActionName("Default")]
public DtoModel Get(int id)
{
return this.decoratee.Get(id);
}
}
Define the custom implementation of IHttpControllerSelector
public class CustomControllerSelector : DefaultHttpControllerSelector
{
private readonly HttpConfiguration configuration;
public CustomControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
this.configuration = configuration;
}
public override HttpControllerDescriptor SelectController(
HttpRequestMessage request)
{
var controllerTypes = this.configuration.Services
.GetHttpControllerTypeResolver().GetControllerTypes(
this.configuration.Services.GetAssembliesResolver());
var matchedTypes = controllerTypes.Where(i =>
typeof(IHttpController).IsAssignableFrom(i)).ToList();
var controllerName = base.GetControllerName(request);
var matchedController = matchedTypes.FirstOrDefault(i =>
i.Name.ToLower() == controllerName.ToLower() + "controller");
if (matchedController.Namespace == "WebApiTest.Controllers")
{
Type decoratorType = typeof(MyHttpControllerDecorator<>);
Type decoratedType = decoratorType.MakeGenericType(matchedController);
return new HttpControllerDescriptor(this.configuration, controllerName, decoratedType);
}
else
{
return new HttpControllerDescriptor(this.configuration, controllerName, matchedController);
}
}
}
When registering the controllers, add in the registration of a decorated version of the controller type
var container = new SimpleInjector.Container();
var services = GlobalConfiguration.Configuration.Services;
var controllerTypes = services.GetHttpControllerTypeResolver()
.GetControllerTypes(services.GetAssembliesResolver());
Type decoratorType = typeof(MyHttpControllerDecorator<>);
foreach (var controllerType in controllerTypes)
{
if (controllerType.Namespace == "WebApiTest.Controllers")
{
Type decoratedType = decoratorType.MakeGenericType(controllerType);
container.Register(decoratedType, () =>
DecoratorBuilder(container.GetInstance(controllerType) as dynamic));
}
else
{
container.Register(controllerType);
}
}
Register the implementation of IHttpControllerSelector
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerSelector),
new CustomControllerSelector(GlobalConfiguration.Configuration));
This is the method for creating the Decorated instance
private MyHttpControllerDecorator<T> DecoratorBuilder<T>(T instance)
where T : IHttpController
{
return new MyHttpControllerDecorator<T>(instance);
}

Categories

Resources