I have a business-process(BMPN) that needs to make two http calls. The first one is used for authentication, it contains a login and a password in its body, and its response contains cookies. Then I take these cookies and put them in the second request. Basically, the purpose of the pair is first to authenticate, then to upload a file. But there can be many such files to be uploaded, each file will have its own instance of the business-process, so there can be many of these instances at the same time.
My service currently looks something like this:
class FileUploadService {
private readonly string _appUrl;
private readonly string _authServiceUrl;
private readonly string _userName;
private readonly string _userPassword;
private readonly IHttpClientFactory _httpClientFactory;
private IEnumerable<string> _cookies;
public FileUploadService(string appUrl, string userName, string userPassword) {
_appUrl = appUrl;
_authServiceUrl = _appUrl + @"/ServiceModel/AuthService.svc/Login";
_userName = userName;
_userPassword = userPassword;
var serviceCollection = new ServiceCollection();
serviceCollection.AddHttpClient("MyHttpClient").ConfigurePrimaryHttpMessageHandler(
configureHandler: () => new HttpClientHandler {
UseCookies = false
});
var serviceProvider = serviceCollection.BuildServiceProvider();
_httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
}
public void TryAuthnenticate() {
HttpClient httpClient = _httpClientFactory.CreateClient("MyHttpClient");
string requestBody = @"{
""UserName"":""" + _userName + @""",
""UserPassword"":""" + _userPassword + @"""
}";
HttpContent content = new StringContent(requestBody, Encoding.UTF8, "application/json");
HttpResponseMessage response = httpClient.PostAsync(_authServiceUrl, content).Result;
_cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value;
}
public void TryUploadFile() {
HttpClient httpClient = _httpClientFactory.CreateClient("MyHttpClient");
httpClient.BaseAddress = new Uri(_appUrl);
string filePath = @"C:\Users\defaultUser\Desktop\12345.pdf";
string url = getUrl(filePath);
MultipartFormDataContent content = getContent(filePath);
HttpResponseMessage response = httpClient.PostAsync(url, content).Result;
}
private string getUrl(string fileInfo) {
// some code that returns a string url
return "";
}
private MultipartFormDataContent getContent(string filePath) {
MultipartFormDataContent content = new MultipartFormDataContent();
byte[] fileBytes = File.ReadAllBytes(filePath);
ByteArrayContent fileContent = new ByteArrayContent(fileBytes);
FileInfo fileInfo = new FileInfo(filePath);
content.Add(fileContent, "12345", fileInfo.Name);
long length = fileInfo.Length;
long rangeEnd = length - 1;
content.Headers.Add("Content-Range", $"bytes 0-{rangeEnd}/{length}");
string cookieValue = "";
foreach (var cookie in _cookies) {
var firstCookieParts = cookie.Split(';');
var secondCookieParts = firstCookieParts[0].Split('=');
cookieValue = cookieValue+$"{secondCookieParts[0]}={secondCookieParts[1]}; ";
}
content.Headers.Add("Cookie", cookieValue);
return content;
}
}
My idea here is that each instance of my business-process will use this service like that:
FileUploadService fileUploadService = new FileUploadService("https://myhost.com", "admin", "test");
fileUploadService.TryAuthnenticate();
fileUploadService.TryUploadFile();
The code does work, meaning the file gets uploaded, but I'm not sure if it won't break with time. There are a few things in particular that I'm worried about.
It is my understending that since I need a lot of HttpClients and they don't need to be long-lived, It is better to use IHttpClientFactory. Since, CookieContainer can be shared among the pool, I want to get rid of CookieContainer and send cookies as a header, hence
UseCookies = false. But would it be the same/better/worse if I used one static HttpClient withUseCookies = falseand cookies would be send as a header?The way I instantiating IHttpClientFactory, is it correct? 95% of the information I've been able to find about it was about ASP.NET Core, but I use .NET Core and I'm not allowed to turn it into ASP.NET Core. So, I have little understanding if the way I do that is correct.
Should IHttpClientFactory be static? Since there will be more than one instance of the service, there will be more than one instance of IHttpClientFactory which feels wrong, but I have not seen example of using a static IHttpClientFactory, so I'm not sure if there is anything wrong with that.
When adding a named client my idea was that I need to somehow make all my Http Clients not to use CookieContainer, so I made this MyHttpClient client. What I don't understand is will it be one and the same instance of HttpClient every time I call CreateClient("MyHttpClient")? Will it break the whole idea of having multiple clients? If so, how do I make the factory to give me only clients that have UseCookies set to false?
The service seems to be working but I'm not sure if I understand how to work with HttpClient and IHttpClientFactory. I want this thing not to run out of available sockets.
Well if you are not making many custom configurations of named HttpClient and need to use those across different classes then there is no need to use
IHttpClientFactory.. If you have only 1 configuration, and want all instances ofFileUploadServiceto use clients with that same configuration, then you can create HttpClient inside the class and configure it there. Take a look at this about usage of named clients https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-8.0#named-clientsIHttpClientFactoryIt depends what one considers correct. I would say it would be better to do the binding of the service in separate class where you do DI stuff, and your
FileUploadServiceconstructor should have parameter ofIHttpClientFactory clientFactory. Then when asking forFileUploadServicefrom your DI container, your configured DependencyResolver will try to get instance of the class that implements IHttpClientFactory and pass it to the constructor ofFileUploadService.In your case I don't believe it will matter if the factory is static or not, even when you have more than 1 instance of
FileUploadService. Because you are only using theCreateClientmethod from the factory, which actually creates and returns a new instance ofHttpClient. So you will always get a new instance of client. The more appropriate question here would be should you use 1 instance ofHttpClient? regarding that you can find some useful info here: https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/What is the overhead of creating a new HttpClient per call in a WebAPI client?
Well you can actually check that easily, while debugging just call it multiple times, and use
Object.ReferenceEquals()on the clients you've created. Finally if you decide to use 1 HttpClient instance, then I don't see a purpose to use the factory.