When working with a HttpClient instance and HttpClientHandler instance (.Net Framework, not using Core), is it possible to access the HttpClientHandler instance/its properties later in any way (for read-only purposes)?
Unfortunately creating the HttpClientHandler instance as a variable for referencing later isnt possible as the HttpClient instance is being passed to a library as a param and we cannot change calling client.
For example, I would like to achieve something like so:
// Created in client we cant modify
var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = True, PreAuthenticate = True });
// Class we can modify
public void DoSomething(HttpClient client)
{
if (client.HttpClientHandler.UseDefaultCredentials == True) Console.WriteLine("UseDefaultCredentials: True");
}
Let me re-edit my answer. Using the constructor
public class MyClass {
private readonly HttpClientHander _handler;
private readonly HttpClient _client;
public MyClass() {
_handler = new HttpClientHander() { /* Initialize the properties */ }
_client = new HttpClient(_handler);
}
public void MyMethod() {
if (_handler.TheProperty == true;) // Do something
}
}
I hope this makes sense. I'm sure it works because of the way objects are referrenced.
Related
I'm relatively new to using HttpClient and API's but I've done some searching around and it seems that the general consensus even from microsoft docs is that HttpClient should be instantiated once. I have multiple places where I need to make calls to an API endpoint in my project so I made an HttpClient utility class that can be called.
public static class HttpClientManager
{
private static HttpClient _httpClient;
private static HttpClientHandler _handler;
private static readonly string _apiBaseUrl = ConfigurationManager.AppSettings["ApiUrl"];
static HttpClientManager()
{
if (_handler == null) _handler = new HttpClientHandler
{
UseDefaultCredentials = true,
PreAuthenticate = true,
AllowAutoRedirect = true,
ClientCertificateOptions = ClientCertificateOption.Automatic
};
if (_httpClient == null) _httpClient = new HttpClient(_handler, false);
}
public static HttpClient GetHttpClient()
{
if(_httpClient.BaseAddress == null)
{
_httpClient.BaseAddress = new Uri(_apiBaseUrl);
}
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return _httpClient;
}
}
The general idea is that the static constructor initializes the http client and then I have a static method GetHttpClient that will set the address and headers and return the client back which other classes in my project can then make a call like so HttpClientManager.GetHttpClient().GetAsync(.....)....
In theory this should ensure that the client gets initialized once on the first call in the static constructor and should re-use it throughout the life of the application. Is this the proper way of doing something like this or are there better designs?
Any advice would be appreciated, thanks!
We've got 20 microservices that are all making use of a client library.
This library simply provides a way for the microservice to connect to its destination: CRM Dynamics.
Within each service we have a wrapper that calls this library:
public static class Client
{
public static HttpClient Instance;
static Client()
{
Instance = new TheCrmClient<AdfsAuthenticationHelper>(MyConfigSettings).HttpClient;
}
}
Within each service we use the client like so:
//Issue request
var response = Client.Instance.SendAsync(request).Result;
After the ADFS session has timed out, we will get a 401 response.
How do we force re-authentication when receiving a 401 response?
The authentication occurs in GetHttpsClient():
public class TheCrmClient<T> where T : IAdfsAuthenticator
{
private readonly RestCRMClientConfiguration _configuration;
private IAdfsAuthenticator _authenticator;
private HttpClient _https;
public TheCrmClient(RestCRMClientConfiguration configuration)
{
_configuration = configuration;
}
public HttpClient HttpClient => GetClientHttps();
private void InitializeAuthenticator(HttpClient client)
{
_authenticator = Activator.CreateInstance(typeof(T), client) as IAdfsAuthenticator;
}
private HttpClient GetClientHttps()
{
var clientHandler = new HttpClientHandler
{
UseCookies = true,
CookieContainer = new CookieContainer(),
AllowAutoRedirect = false
};
_https = new HttpClient(clientHandler)
{
BaseAddress = _configuration.GetServiceUri(RestCRMClientConfiguration.UrlProtocolType.Https)
};
if (_authenticator == null)
InitializeAuthenticator(_https);
_authenticator.Authenticate(_configuration.GetNetworkCredential(),
_configuration.GetAuthenticationTestEndpointUrl(RestCRMClientConfiguration.UrlProtocolType.Https));
return _https;
}
}
To clarify the lifecycle of the request is:
Microservice.Controller --> Microservice.ClientWrapper --> TheCrmClientLibrary --> CrmDestination
Is it possible to create one instance of httpclient in Xamarin Forms application in OnStart() and use it everywhere in my application?
Yes you can use the same httpclient for all request in your app. But you will need to take note that if there is API that is having different base URL or headers information, then you will need to create another httpclient for that.
What I do is I have a class to manage the HttpClient instances. If there is no instance that is matching the HttpConfig, it will create and store it. If there is already an existing instance, it will just return it.
Example of code (HttpService is dependency injected):
public class HttpService : IHttpService
{
private static readonly int MAX_CLIENT = 5;
private Dictionary<HttpConfig, HttpClient> mClients;
private Queue<HttpConfig> mClientSequence;
public HttpService()
{
mClients = new Dictionary<HttpConfig, HttpClient>();
mClientSequence = new Queue<HttpConfig>();
}
private HttpClient CreateHttpClientAsync(HttpConfig config)
{
HttpClient httpClient;
if (mClients.ContainsKey(config))
{
httpClient = mClients[config];
}
else
{
// TODO: Create HttpClient...
if (mClientSequence.Count >= MAX_CLIENT)
{
// Remove the first item
var removingConfig = mClientSequence.Dequeue();
mClients.Remove(removingConfig);
}
mClients[config] = httpClient;
mClientSequence.Enqueue(config);
}
return httpClient;
}
...
}
HttpConfig is class where I store BaseUrl, Timeout, Headers, Auth info, etc. You will need to override the Equals method in the class for comparison whether there is existing same config.
public override bool Equals(object obj)
{
// Logic to determine whether it is same config
}
I want to set a default header for every method in the UserHttpClient but I don`t want that every method is doing that, I want to do it in a general way.
The problem I see with the current implementation is, that when I call one method the _client gets disposed thus at the next call within a Http Request the _client is not initialized, as this happens within the constructor.
The UserHttpClient is registered via DI as per Http Request.
I also do not want to create a private/base method where I pass the _client and do the header addition there.
How would you solve that problem?
public class UserHttpClient : IUserRemoteRepository
{
private readonly string baseUrl = ConfigurationManager.AppSettings["baseUrl"];
private readonly string header = ConfigurationManager.AppSettings["userHeader"];
private readonly HttpClient _client;
public ServiceProductDataProvider(string toolSystemKeyHeader)
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Add(header, token);
}
public async Task<List<UserDto>> GetUsers(UserRequestDto dto)
{
using (_client)
{
// do stuff
var users = await _client.GetAsync("url here");
}
}
public async Task<UserDto> GetUser(Guid userId)
{
using (_client)
{
// do stuff
var users = await _client.GetAsync("url here");
}
}
}
The class UserHttpClient has a member that is IDisposable (private readonly HttpClient _client;). That means that the UserHttpClient should also implement IDisposable:
public void Dispose()
{
_client.Dispose();
}
Then, the class/code that is using UserHttpClient is responsible for Disposing it after it's done with it. If the instance is injected, then the DI framework you use probably handles disposing it automatically at the end of the request. What's left for you then is to simply remove the using blocks from the implementation:
public async Task<List<UserDto>> GetUsers(UserRequestDto dto)
{
// do stuff
var users = await _client.GetAsync("url here");
}
---- EDIT ----
You could also work around the issue by not reusing the HttpClient:
private string _toolSystemKeyHeader;
public ServiceProductDataProvider(string toolSystemKeyHeader)
{
_toolSystemKeyHeader = toolSystemKeyHeader
}
private HttpClient GetClientInstance()
{
HttpClient _client = new HttpClient();
_client.DefaultRequestHeaders.Add(header, _toolSystemKeyHeader); //?? in your original code, the toolSystemKeyHeader is not used, but I guess it is the token..?
return _client;
}
And:
public async Task<List<UserDto>> GetUsers(UserRequestDto dto)
{
using (var _client = GetClientInstance())
{
// do stuff
var users = await _client.GetAsync("url here");
}
}
I have an implementation of an authenticated HttpClient generator class that resembles this:
public class X509Authentication : IClientAuthenticator
{
protected readonly X509Certificate2 Certificate;
public X509Authentication(X509Certificate2 certificate)
{
if (certificate == null) throw new ArgumentNullException("certificate");
Certificate = certificate;
}
public HttpClient GenerateClient()
{
var clientHandler = new WebRequestHandler();
clientHandler.ClientCertificates.Add(Certificate);
var request = new HttpClient(clientHandler);
return request;
}
public void Dispose()
{
//nothing to do here.
}
}
... how can I test that the GenerateClient() method has successfully attached the client certificate to the HttpClient class?
[TestMethod]
public void HttpClientCreationIncludesCertificate()
{
using (var auth = new X509Authentication(_certificate))
using (var client = auth.GenerateClient())
{
Assert...what? The certificate(s) are not visible here.
}
}
...or am I trying to test the wrong thing?
9 month old question but anyhow :)
What I would do. You are thinking correctly with the interface. Now that you have an interface you can mock away the "real" implementation of GenerateClient since this method doesn't do anything else then using someone else's code (Code that that's not very test-friendly in the first place).
What I would test in this situation is that the method that should call IClientAuthenticator.GenerateClient really calls it. An example ->
[TestMethod]
Public void MyClass_MymethodthatcallsGenereateClient_DoesCallGenerateClient()
{
// Arrange
Mock<IClientAuthenticator> clientAuth = new Mock<IClientAuthenticator>();
MyClass myclass = new MyClass()
// Act
var result = MyClass.MymethodthatcallsGenereateClient();
// Assert (verify that we really added the client)
clientAuth.Verify(x => x.GenerateClient);
}
Now we can be safe knowing that the client certificate is added when it should be.
Hope this helps!