Reputation: 685
I need to use HttpClientFactory
for connection to external api.
The code in Startup.cs
looks this
public void SetUpHttpClients(IServiceCollection services)
{
var loginEndpoint = Path.Combine(baseApi, "api/authentication);
var fileExists = File.Exists(certificatePath);
if (!fileExists)
throw new ArgumentException(certificatePath);
var certificate = new X509Certificate2(certificatePath, certPwd);
services.AddHttpClient("TestClient", client =>
{
client.BaseAddress = new Uri(baseApi);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(accept));
client.DefaultRequestHeaders.Add("ApiKey", apiKey);
var body = new { Username = username, Password = password };
var jsonBody = JsonConvert.SerializeObject(body);
var content = new StringContent(jsonBody, Encoding.UTF8, contentType);
var loginResponse = client.PostAsync(loginEndpoint, content).Result;
}).ConfigurePrimaryHttpMessageHandler(() =>
{
var cookieContainer = new CookieContainer();
var handler = new HttpClientHandler
{
CookieContainer = cookieContainer
};
handler.ClientCertificates.Add(certificate);
return handler;
});
I've added this code in MessageController with HttpClientFactory,
public class MessagesController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
public ValuesController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet]
public IActionResult GetMessages()
{
var client = _httpClientFactory.CreateClient("TestClient");
var result = client.GetStringAsync("/api/messages");
return Ok(result);
}
}
but when I try to connection and get messages from HttpClientFactory
with I get this error:
Failed to load resource: net::ERR_CONNECTION_RESET, 404 - BadRequest
Upvotes: 1
Views: 1504
Reputation: 685
I've resolve this issue. SetUpHttpClient
method look this:
public void SetUpHttpClients(IServiceCollection services)
{
var basePath = Directory.GetCurrentDirectory();
var certificatePath = Path.Combine(basePath, CertPath);
var fileExists = File.Exists(certificatePath);
if (!fileExists)
throw new ArgumentException(certificatePath);
var certificate = new X509Certificate2(certificatePath, CertPwd);
services.AddHttpClient("TestClient", client =>
{
client.BaseAddress = new Uri(BaseApi);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Accept));
client.DefaultRequestHeaders.Add("ApiKey", ApiKey);
}).ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
return handler;
});
}
The rest of code for login and messages I've added to service and call from controller.
MessageClient.cs
public MessageClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
_client = _httpClientFactory.CreateClient("TestClient");
var body = new { UserName = Username, UserPassword = Password };
var jsonBody = JsonConvert.SerializeObject(body);
var content = new StringContent(jsonBody, Encoding.UTF8, ContentType);
var loginEndpoint = Path.Combine(BaseApi, "api/authentication");
_responseMessage = _client.PostAsync(loginEndpoint, content).Result;
}
public async Task<string> ReadMessages()
{
try
{
string messages = "";
if (_responseMessage.IsSuccessStatusCode)
{
var messagesEndpoint = Path.Combine(BaseApi, "api/messages/");
var cookieContainer = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cookieContainer };
var cookiesToSet = GetCookiesToSet(_responseMessage.Headers, BaseApi);
foreach (var cookie in cookiesToSet)
{
handler.CookieContainer.Add(cookie);
}
var messageResponse = await _client.GetAsync(messagesEndpoint);
messages = await messageResponse.Content.ReadAsStringAsync();
}
return messages;
}
catch (Exception e)
{
throw e.InnerException;
}
}
MessageContoller.cs
[HttpGet]
public async Task<ActionResult> GetMessages()
{
var result = await _messageClient.ReadMessages();
return Ok(result);
}
Thanks @Nkosi.
Upvotes: 1
Reputation: 247511
You trying to make a request before completing the factory configuration That Post will fail because you are trying to use the client while still configuring it.
The assumption here is that SetUpHttpClients
is being called within ConfigureServices
.
public void ConfigureServices(IServiceCollection services) {
//...
SetUpHttpClients(services);
//...
services.AddMvc();
}
public void SetUpHttpClients(IServiceCollection services) {
var basePath = Directory.GetCurrentDirectory();
var certificatePath = Path.Combine(basePath, certPath);
var fileExists = File.Exists(certificatePath);
if (!fileExists)
throw new ArgumentException(certificatePath);
var certificate = new X509Certificate2(certificatePath, certPwd);
//Adding a named client
services.AddHttpClient("TestClient", client => {
client.BaseAddress = new Uri(baseApi);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(accept));
client.DefaultRequestHeaders.Add("ApiKey", apiKey);
})
.ConfigurePrimaryHttpMessageHandler(() => {
var cookieContainer = new CookieContainer();
var handler = new HttpClientHandler {
CookieContainer = cookieContainer
};
handler.ClientCertificates.Add(certificate);
return handler;
});
}
With the configuration done, the factory is available for injection into a controller or service class as needed.
The following example shows using the factory as a dependency to a controller.
[Route("api/[controller]")]
public class ValuesController : Controller {
private readonly IHttpClientFactory factory;
public MyController(IHttpClientFactory factory) {
this.factory = factory;
}
[HttpGet]
public async Task<IActionResult> Get() {
var client = factory.CreateClient("TestClient");
var result = client.GetStringAsync("api/messages");
return Ok(result);
}
}
In the above the factory is used to create an instance of the named client. It will have all the configuration that was set during start up, which would include the client handler with certificate.
To be able to use the client during start up (which I would advise against), you would first need to build the service collection into a service provider.
var serviceProvider = services.BuildServiceProvider();
var factory = serviceProvider.GetService<IHttpClientFactory>();
var client = factory.CreateClient("TestClient");
var body = new { Username = username, Password = password };
var jsonBody = JsonConvert.SerializeObject(body);
var content = new StringContent(jsonBody, Encoding.UTF8, contentType);
var loginResponse = client.PostAsync("api/authentication", content).Result;
//...do something with the response
This can however have unforeseen results as the service collection would not have been completely populated at this stage in the start up process.
Upvotes: 2