How to set custom DelegatingHandler to all HttpClients automatically? - asp.net-core

I would like to use the LoggingHttpClientHandler for all the clients in the application. I found the only usable way via named/typed client
startup.cs
services.AddTransient<LoggingHttpClientHandler>();
services.AddHttpClient("clientWithLogging").AddHttpMessageHandler<LoggingHttpClientHandler>();
and then in each service I have to use var client = _clientFactory.CreateClient("clientWithLogging") which is kinda uncomfortable.
LoggingHttpClientHandler.cs
public class LoggingHttpClientHandler : DelegatingHandler
{
public LoggingHttpClientHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Content != null)
{
Logging.Log.Info(await request.Content.ReadAsStringAsync());
}
return await base.SendAsync(request, cancellationToken);
}
}
Is there a way how to do it without naming each client?

If it is just about the name, there is an extension method CreateClient that takes no arguments and uses the default name, which is string.Empty.
So you might be fine by registering the service using string.Empty as name, e.g.:
services.AddHttpClient(string.Empty).AddHttpMessageHandler<LoggingHttpClientHandler>();
and instantiate clients using:
var client = _clientFactory.CreateClient();

The problem was with resolving of params in useless constructor of LoggingHttpClientHandler.
So I removed the constructor.

Related

Mediatr Scope problems

I am using Mediatr to handle messages from a queue. I can get a simple example to work. However I have run into problems when I try to inject an object into my handler
public class MessageCommandHandler : IRequestHandler<MessageCommand, bool>
{
private IMyDependency myDependency;
public MessageCommandHandler(IMyDependency myDependency)
{
this.myDependency = myDependency;
}
public Task<bool> Handle(MessageCommand request, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
}
This only works when I register IMyDependency as a transient scope, however when I register it as scoped lifetime it fails with the error
Cannot resolve 'MediatR.IRequestHandler`2[MyNamespace.MessageCommand,System.Boolean]' from root provider because it requires scoped service 'MyNamespace.IMyDependency'
I need to be able to inject dependencies with scoped lifetime. Has anyone got a solution for this.
I am using the .NET Core dependency injection framework. It is setup as follows
services.AddHostedService<QueueConsumer>();
services.AddScoped<IMyDependency, MyDependency>();
services.AddMediatR(new Assembly[] { Assembly.GetExecutingAssembly() });
Any ideas?
Any time you use a dependency with a Scoped lifetime, you will need to use it inside a pre-created scope. In the case of MVC this would happen automatically behind the scenes but if you're using direct from your own code, say via a console application or something, you will need to create the scope yourself.
This can be done by injecting an instance of IServiceScopeFactory and then using this factory to create a scope and then retrieve the dependency from that scope e.g.
public class MessageCommandHandler : IRequestHandler<MessageCommand, bool>
{
private IServiceScopeFactory _serviceScopeFactory;
public MessageCommandHandler(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public Task<bool> Handle(MessageCommand request, CancellationToken cancellationToken)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var myDependency = scopedServices.GetRequiredService<IMyDependency>();
return Task.FromResult(true);
}
}
}
However (and note that the code above is untested), in my own systems I would almost always create the scope around whatever is sending the mediator request in which case any Scoped dependencies will still be injected automatically at this scope e.g.
... // some other calling class / Main method etc..
using (var scope = _serviceScopeFactory.CreateScope())
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
mediator.Send(new MessageCommand());
}

Access IUrlHelper inside DelegatingHandler

Following a migration to ASP.Net core, the following handler does not work. I cannot see how to get access to IUrlHelper from the HttpRequestMessage as was previously possible, and can't find a package with relevant extension methods.
The handler is added using config.MessageHandlers.Add(new LinkDecoratorHandler());
Can anyone help?
public class LinkDecoratorHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage
request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
if (!(response.Content is ObjectContent))
{
return response;
}
var entity = (response.Content as ObjectContent).Value as ILinkedEntity;
var enumeration = (response.Content as ObjectContent).Value as IEnumerable<ILinkedEntity>;
if (entity != null || enumeration != null)
{
//no longer available
var helper = request.GetUrlHelper();
//blah
}
return response;
});
}
}
Thanks in advance
If your LinkDecoratorHandler is instantiated via dependency injection then you could inject an instance of IActionContextAccessor to get the current ActionContext. From there, you can create your own UrlHelper instance.

Does .NET Core HttpClient have the concept of interceptors?

I would like to wrap some timing logic around all calls made through HttpClient from my ASP.NET Core app, including calls made from 3rd party libraries.
Does HttpClient in .NET Core have something I can plug into to run some code on every request?
Yes, it does. HttpClient produces a HTTP request via DelegatingHandler chain. To intercept the HttpClient request, you can add a derived handler with overrided SendAsync method to that chain.
Usage:
var handler = new ExampleHttpHandler(fooService);
var client = new HttpClient(new ExampleHttpHandler(handler));
var response = await client.GetAsync("http://google.com");
Implementation:
public class ExampleHttpHandler : DelegatingHandler
{
//use this constructor if a handler is registered in DI to inject dependencies
public ExampleHttpHandler(FooService service) : this(service, null)
{
}
//Use this constructor if a handler is created manually.
//Otherwise, use DelegatingHandler.InnerHandler public property to set the next handler.
public ExampleHttpHandler(FooService service, HttpMessageHandler innerHandler)
{
//the last (inner) handler in the pipeline should be a "real" handler.
//To make a HTTP request, create a HttpClientHandler instance.
InnerHandler = innerHandler ?? new HttpClientHandler();
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
//add any logic here
return await base.SendAsync(request, cancellationToken);
}
}
BTW, I recommend moving as much business logic out of a custom handler as possible to simplify unit-testing it.

Lightinject with Web API - How can I get the HttpRequestMessage?

How can I get the current HttpRequestMessage when using Lightinject with Web API?
In Simple Injector, for example, I have the following extension method on the container:
var msg = container.GetCurrentHttpRequestMessage()
But I couldn't find a matching one in Lightinject.
If you take a look in the Simple Injector Web API integration source code, you'll find that the implementation is actually really straightforward. You can easily implement this yourself.
What you need is to create a custom DelegatingHandler that stores the current HttpRequestMessage in a way that you can retrieve it later on, as follows:
public sealed class HttpRequestMessageDelegatingHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken) {
CurrentMessage = request;
return base.SendAsync(request, cancellationToken);
}
public static HttpRequestMessage CurrentMessage {
get { return (HttpRequestMessage)CallContext.LogicalGetData("RequestMessage"); }
private set { CallContext.LogicalSetData(value, "RequestMessage"); }
}
}
// Register this handler as follows in Web API
configuration.MessageHandlers.Add(new HttpRequestMessageDelegatingHandler());
Now you can retrieve the request's current message as follows:
HttpRequestMessageDelegatingHandler.CurrentMessage
The Simple Injector documentation advises to hide this call behind a custom abstraction, such as a simple IRequestMessageProvider. An implementation is of course easily created:
private sealed class RequestMessageProvider : IRequestMessageProvider {
public HttpRequestMessage CurrentMessage {
get { return HttpRequestMessageDelegatingHandler.CurrentMessage; }
}
}
Instead of creating an IRequestMessageProvider abstraction plus implementation, you can register it directly as delegate as follows:
container.Register<Func<HttpRequestMessage>>(_ =>
() => HttpRequestMessageDelegatingHandler.CurrentMessage);
My preference is to use an interface instead of a Func<T>, because such interface is much more explicit and readable.

ASP.Net Web API - The remote server returned an error: (405) Method Not Allowed

I am using the new MVC4 ASP.Net Web API system.
I am calling my API in a test project using WebClient. If I use GET or POST, it works fine. If I use anything else, I get Method Not Allowed. I am actually "faking" the method by injecting the following header. I am doing this because my end users will also have to do this due to the limitations of some firewalls.
I am calling the URL via IIS (i.e. not cassini) - e.g. http://localhost/MyAPI/api/Test
wc.Headers.Add("X-HTTP-Method", "PUT");
I tried adjusting the script mappings in IIS, but as there is no extension, I don't know what I am meant to be adjusting!
Any ideas?
Regards
Nick
The X-HTTP-Method (or X-HTTP-Method-Override) header is not supported out of the box by Web API. You will need to create a custom DelegatingHandler (below implementation assumes that you are making your request with POST method as it should be):
public class XHttpMethodDelegatingHandler : DelegatingHandler
{
private static readonly string[] _allowedHttpMethods = { "PUT", "DELETE" };
private static readonly string _httpMethodHeader = "X-HTTP-Method";
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Post && request.Headers.Contains(_httpMethodHeader))
{
string httpMethod = request.Headers.GetValues(_httpMethodHeader).FirstOrDefault();
if (_allowedHttpMethods.Contains(httpMethod, StringComparer.InvariantCultureIgnoreCase))
request.Method = new HttpMethod(httpMethod);
}
return base.SendAsync(request, cancellationToken);
}
}
Now you just need to register your DelegatingHandler in Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new XHttpMethodDelegatingHandler());
...
}
This should do the trick.