Test inner httpClient via WebApplicationFactory - c#

Well, I know this is a bit of a tricky case. I have an ASP.NET Core site backend (site) that communicates with another system (facade) via http.
But not with HttpClient but with clients that facade provides with HttpClient underneath.
Constructors of these clients look like this
public class OrdersClient : IOrdersClient
{
private HttpClient _client;
public OrdersClient(IEndpoint endpoint, HttpClient client)
{ ... }
}
And registration looks like this
services
.AddHttpClient("facade-client")
.AddHttpMessageHandler<CorrelationIdDelegatingHandler>()
.AddHttpMessageHandler<HttpErrorDelegatingHandler>()
.AddTypedClient<IOrdersClient>((endpoint, client) => new OrdersClient(endpoint, client));
And configuration contains (it's important!)
app.UseCorrelationId(new CorrelationIdOptions());
N.B. What endpoint is for in this case is not important.
Ok. So I want to test my site with brand new cool WebApplicationFactory. I create it and get a client to the site as described in the docs. For some tests I just mock clients to facade like
public TestOrdersClient: IOrdersClient
{
}
and test my logic without facade.
Getting to the point. I want to test handlers that decorates my HttpClient to facade.
How I want to test handlers: I register fake PrimaryHttpMessageHandler so it will imitate answers from facade in my WebApplicationFactory. And I added some ImitationClient that used registered HttpClient.
services
.AddHttpClient("facade-client")
.ConfigurePrimaryHttpMessageHandler(() => new HttpMessageHandlerFake())
.AddTypedClient<ImitationClient>();
And here I am in trouble.
In my test a get ImitationClient from factory and try to call some method that calls HttpClient inside.
var factory = new SiteWebApplicationFactory();
var client = factory.CreateDefaultClient();
var imitationClient =factory.Server.Host.Services.GetService<ImitationClient>();
await imitationClient.GetData();
And here I get System.NullReferenceException because of this handler:
public class CorrelationIdDelegatingHandler : DelegatingHandler
{
private readonly ICorrelationContextAccessor _correlationAccessor;
public CorrelationIdDelegatingHandler(ICorrelationContextAccessor correlationAccessor)
{
_correlationAccessor = correlationAccessor;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add(
_correlationAccessor.CorrelationContext.Header,
_correlationAccessor.CorrelationContext.CorrelationId);
return base.SendAsync(request, cancellationToken);
}
}
Correlation Id assigned to incoming request to site and then forwarded to requests from site to facade. But when I test only client to facade I don't have incoming request, don't have CorrelationContext and fail.
Finally my question is how to correctly write test for inner httpClient with WebApplicationFactory. I know my description is rather confusing, I apologize for that. I am ready to answer any questions on the code and architecture of our project. Thank you!

Related

Dependency Injection - HttpClient and OAuth2

I’m creating a class library (.NET Standard 2) where I need to pass a HttpClient to the constructor (Dependency Injection).
The class library is calling a third-party API using OAuth2 (It requests their API – with ClientID and ClientSecret – and get a token back which I’m going to use for subsequent calls).
I have some problems figuring out how to do the OAuth2 “stuff” with dependency injection.
The project I’m using the class library in – sets up the dependency in the StartUp class like: Services.AddHttpClient()
Can I somehow attach the OAuth2 “stuff” to the HttpClient?
What you could be doing is using a DelegatingHandler:
interface IOAuth2Stuff
{
string Token { get; set; }
}
class OAuth2StuffRequestHandler : DelegatingHandler, IOAuth2Stuff
{
public string Token { get; set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Add OAuth2 stuff to request
var token = Token;
return await base.SendAsync(request, cancellationToken);
}
}
Configure your services:
services.AddSingleton<OAuth2StuffRequestHandler>();
services.AddSingleton<IOAuth2Stuff>(provider =>
provider.GetRequiredService<OAuth2StuffRequestHandler>());
services.AddHttpClient("ServiceHttpClient")
.AddHttpMessageHandler<OAuth2StuffRequestHandler>();
And in your service class:
class Service
{
public Service(IHttpClientFactory clientFactory, IOAuth2Stuff oauth2)
{
var client = clientFactory.CreateClient("ServiceHttpClient");
// Do oauth2
// Set oauth2 token to request handler.
oauth2.Token = "blah blah";
}
}
Note that the lifetime of the middleware isn't perfect in the example.
One way you can achieve this is with a DelegatingHandler that you can add to the HTTP Client
This answer - https://stackoverflow.com/a/56204930/1538039 - describes adding the message handler to the HttpClient instance retrieved from a HttpClientFactory.
Within the AuthenticationDelegatingHandler class, it makes a call to GetToken -> which is something you'd need to provide that takes care of token retrieval/caching/renewal, use one of the the prebuilt nuget packages that can do this for you. (i.e. https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/ or equivalent)

A proxy web service to a web service - where am I missing?

I program ASP.NET Framework MVC and Web API 2
I have to access a REST service for some information. The nature of the security requirements for this service require that I ask from a limited set of known IP addresses. The nature of my client requirements is that there will be an unknown number of them with IPs that are assigned by some DHCP. I think I need to stand up a proxy that will forward requests to the service and return responses to the client that asked. This server can be assigned a single static IP, that I can register with the target service. I don't want to try to duplicate the signatures of the target service and have to maintain my proxy whenever they decide to improve interfaces.
I would have the service that is restricting IPs and accepts a GET for http://S/action as an example. I would have the proxy at http://P/action. The client would send GET http://P/action and P would, in response, send GET http://S/action, collect the response, return it back to the client.
An attempt to implement this strategy, here is a handler I built for P that doesn't work:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
DelegatingHandler handler = new DelegatingHandlerProxy<ProxyHandler>();
config.MessageHandlers.Add(handler);
}
}
DelegatingProxyHandler is a way to get my dependency injection container involved:
public sealed class DelegatingHandlerProxy<THandler> : DelegatingHandler
where THandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
IDependencyScope scope = request.GetDependencyScope();
Task<HttpResponseMessage> task;
if (scope.GetService(typeof(THandler)) is DelegatingHandler handler)
{
if (!ReferenceEquals(handler.InnerHandler, InnerHandler))
{
handler.InnerHandler = InnerHandler;
}
HttpMessageInvoker invoker = new HttpMessageInvoker(handler);
task = invoker.SendAsync(request, cancellationToken);
}
else
{
throw new InvalidOperationException("Handler not registered with DI container");
}
return task;
}
}
The ProxyHandler that I want to do the work is:
public class ProxyHandler: DelegatingHandler
{
public ProxyHandler(
ITransformRequest preProcessor,
ITransformResponse postProcessor,
IForwardRequest forwarder)
{
PreProcessor = preProcessor;
PostProcessor = postProcessor;
Forwarder = forwarder;
}
private ITransformRequest PreProcessor { get; }
private ITransformResponse PostProcessor { get; }
private IForwardRequest Forwarder { get; }
#region Overrides of DelegatingHandler
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (PreProcessor != null)
{
request.RequestUri = PreProcessor.Transform(request.RequestUri);
}
HttpResponseMessage response = await Forwarder.Forward(request, cancellationToken);
HttpResponseMessage transformedResponse = PostProcessor.Transform(response);
return transformedResponse;
}
#endregion
}
In this case, the DI container supplies a PreProcessor that changes host, port, and prefix of the request to the target service. The Forwarder sends the request to the target using HttpClient. The PostProcessor will be a noop.
I didn't build any controllers. My thinking is that if this pipeline behaves as I expect, there won't be any controller that needs invoking. When I send ant request to this, http://P/anything returns a 404, not htto://S/anything. What am I missing?
Any particular reason you're not just writing a set of matching controllers that accept client requests and then execute the equivalent request on the 3rd arty API using a service that implements a simple web client and then returning the responses - perhaps including some authentication & caching logic to lower the impact on their API?
If your 3rd party API provider is limiting requests by IP, that is likely because they trust (or explicitly require) you to manage requests to their API in order to protect it from excessive load and/or security risks. Directly forwarding all client requests without any logic in your middleware means you're negating this limitation.
If the only purpose of your application is to provide a static IP (and you do not need to add any logic in your code) then you should consider using one of the many off the shelf API gateway products - e.g. Kong, which is an open source and very well established with plenty of community support https://konghq.com/kong-community-edition/

Good way to implment a .NET Service layer pattern with REST calls?

I'm trying to come up with a good way to have a service layer that is mainly making REST API calls to an external API. I've currently got a .NET Core 2.0 project where my services are being injected into my controllers, and I'm making calls that way. However, in my services themselves, I'm making calls to external APIs that require an access token. My current architecture has mostly been thrown together pretty quickly just to kind of "get things working", but now I was to decouple things a bit more and make it more testable. Here is an example of one of my service methods to illustrate where I'm at, an implementation of ISomeSearchService:
public async Task<SearchDataResponse> SearchAsync(string query, string accessToken)
{
SearchDataResponsedataResponse = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("www.somesite.com/api");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken);
IList<KeyValuePair<string, object>> parameters = new List<KeyValuePair<string, object>>()
{
KeyValuePair.Create<string, object>( "searchTerm", query ),
};
var response = await client.GetAsync("/search" + UriFormatter.AsQueryString(parameters));
if(response.IsSuccessStatusCode)
{
string responseBody = await response.Content.ReadAsStringAsync();
dataResponse = JsonConvert.DeserializeObject<SearchDataResponse>(responseBody);
}
}
return dataResponse;
}
I know this is riddled with issues. Namely one of the biggest to me is that is constructing this HTTP client in the service itself. I would like to abstract that out, and maybe pass in a client to the service that is already constructed some way. That way in my tests, I can pass in mock clients to be able to test these methods without having to make actual HTTP calls. I'm not sure if there is a better way to handle that though. I can't find much guidance on service layers that are making HTTP calls. Most documentation I find is related to calling a DB directly.
Another issue is that I don't like passing the access token directly into the service. Since I was limited on time, I just did that to get things working, but I'm not happy with it.
Does anyone have some experience with this or a design that I could look into that would decouple this out more?
The goal is to inject an HttpClient instance. You can extract an interface from HttpClient to assist with mocking.
public interface IHttpClient : IDisposable
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
}
You will need an implementation that constructs instances of HttpClient but you can also use the interface to mock the request.
Now your service codes against the IHttpClient
public class SearchService : ISomeSearchService
{
private readonly IHttpClient httpClient;
public SearchService(IHttpClient httpClient)
{
this.httpClient = httpClient;
}
}
Alter your request so that you send an HttpRequestMessage instead of using .GetAsync() that way you can alter the Authorization header per request.
public async Task<SearchDataResponse> SearchAsync(string query, string accessToken)
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri)
{
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
var response = await httpClient.SendAsync(request);
...
}
}
Then register the singleton HttpClient
services.AddSingleton<IHttpClient, HttpClientFactory>();
services.AddScoped<ISomeSearchService, SearchService>();

Singleton HttpClient calling GetAsync/SendAsync with async/await never returns

I am building an application using Xamarin (Android), it uses a PCL project as a Service layer. I have a Web Api endpoint and I am using HttpClient to consume it.
Everything works fine, but if I leave my Android app open and idle for a while (like 2 minutes) and I try to make a new request, the first request using the singleton HttpClient won't work. It just never returns and stays there until it timeouts (TaskCancelledException). I also put a breakpoint on my Api and it doesn't get hit. If I try to send the request again, then it works.
After a lot of debugging I found that this only happens if I try to use the HttpClient as a Singleton. If I create a new HttpClient for every request everything works.
At first I thought this was a deadlock issue, I've done a lot of research and double checked everything following the guidelines described in this other answer and Stephen Cleary's excellent post and I'm almost sure this is not the case.
I'm using ConfigureAwait(false) in every call from my PCL project so it doesn't capture the context.
The flow of a request goes like this:
Inside an Android Fragment:
SampleService svc = new SampleService();
response = await svc.GetAllSamples();
The service called (in my PCL project):
public class SampleService
{
public HttpClient Client { get; set; }
public SampleService()
{
// resolves my singleton instance and uses my custom DelegatingHandler
Client = CustomHttpClient.Instance;
}
public async Task<IEnumerable<Sample>> GetAllSamples()
{
IEnumerable<Sample> list = null;
// this never returns and timeouts the first time
using (var response = await Client.GetAsync("samples").ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
lista = await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<Sample>>(json)).ConfigureAwait(false);
}
return list;
}
}
}
This is how I build my Singleton instance:
public sealed class CustomHttpClient
{
private static HttpClient _client;
public static HttpClient GetClient()
{
if (_client == null)
{
HttpMessageHandler messageHandler = new HttpClientHandler();
_client = new HttpClient(messageHandler);
_client.Timeout = TimeSpan.FromSeconds(30);
_client.BaseAddress = new Uri("myendpoint");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
return _client;
}
}
I tried to simplify and isolate the code here, if I can provide any other useful snippets, just let me know.
Am I doing something wrong regarding singletons HttpClient that I'm not aware of?
Update: Just for clarification, I'm trying to use HttpClient as a Singleton just because, as I found in this answer by Darrel Miller and in the book Designing Evolvable Web APIs with ASP.NET (Chapter 14), they were designed to be reusable and thread-safe (in most of the cases). From my research I'm not using anything that is not thread-safe in it.

How to pass Owin context to a Repo being injected into Api controller

I've got a MVC WebApi owin (soft hosted) project, that uses Unity for resolving controller dependencies
which look like this
public class PacientaiController : ODataController
{
private readonly IEntityRepo<Ent.Pacientas> repo;
public PacientaiController(IEntityRepo<Ent.Pacientas> repo)
{
this.repo = repo;
}
the problem I'm trying to solve - is how do I pass 'OwinContex' into a Repo.
public class PacientasEntityRepo:IEntityRepo<Pacientas>,IDisposable
{
public PacientasEntityRepo(IOwinContext ctx)
{
.........
If I try to register it like this in the Startup.cs
Container.RegisterType<IOwinContext>(new InjectionFactory(o => HttpContext.Current.GetOwinContext()));
I get a null ref, saying that HttpContext.Current is NULL
The main idea here, is to pass the currently authenticated user to the repo, because Repo host the Logic for querying the Database, depending on the user. (say if the user is Admin, then return this data, if the user is guest - return this data)
The point being - that this is a self Host !
Lets put aside why you have this design and concentrate to the problem: injecting the IOwinContext:
you can also get it from a HttpRequestMessage instance with the GetOwinContext method, however you also need to get a HttpRequestMessage somehow.
Unity does not support injection of the HttpRequestMessage out of the box but you can use a custom DelegatingHandler which stores the current HttpRequestMessage in the container as described here: Inject WebAPI UrlHelper into service using Autofac
The linked question is about Autofac but you can transfer it for work with Unity:
The CurrentRequest and the CurrentRequestHandler can be used from Andrew Davey's answer as it is:
public class CurrentRequest
{
public HttpRequestMessage Value { get; set; }
}
public class CurrentRequestHandler : DelegatingHandler
{
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var scope = request.GetDependencyScope();
var currentRequest = (CurrentRequest)scope.GetService(typeof(CurrentRequest));
currentRequest.Value = request;
return await base.SendAsync(request, cancellationToken);
}
}
Then you just need to register the DelegatingHandler with:
httpConfiguration.MessageHandlers.Insert(0, new CurrentRequestHandler());
And register the CurrentRequest and IOwinContext in the container
container.RegisterType<CurrentRequest>(
new HierarchicalLifetimeManager());
container.RegisterType<IOwinContext>(
new HierarchicalLifetimeManager(),
new InjectionFactory(c => c.Resolve<CurrentRequest>().Value.GetOwinContext()));
httpConfiguration.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
Beside the custom delegation handler there are other places to hook into Web.API to capture the HttpRequestMessage for example you can create your own IHttpControllerActivator and use the ExecuteAsync method as described here: Dependency Injection in ASP.NET Web API 2
In a selfhosted application you do not have a HttpContext. You need an other way to move the state around. An option is to use a self implemented HttpContext like:
https://github.com/danielcrenna/graveyard/tree/master/httpcontext-shim
I think the problem is that HttpContext does not exist at the time Startup is called, so what you probably need, is to have a Func instead, like this:
public class PacientasEntityRepo:IEntityRepo<Pacientas>,IDisposable
{
public PacientasEntityRepo(Func<IOwinContext> ctx)
{
.........
and then change the code in Startup to this:
Container.RegisterType<IOwinContext>(new InjectionFactory(() => HttpContext.Current.GetOwinContext()));
In my Asp.Net Mvc (AutoFac) project (not core) i have used below registeration andthat was successed
builder.RegisterType<OwinContext>().AsImplementedInterfaces().InstancePerRequest();

Categories