HttpClientFactory throws an error on client setup - c#

How is it possible that I get this error message in the setup of a client when I use HttpClientFactory? Who is already using this client?
I've been trying both like this as a named client and also by specifying the <Interface,Class> so it can add it as a transient - same error.
services.AddHttpClient("xxx", async client =>
{
var tokenData = await jwtTokenProvider.GetTokenAsync(appSettingsSectionIdentity);
if (tokenData is null || !tokenData.IsValid)
throw new TechnicalErrorException("Can't get a token xxx");
client.CancelPendingRequests();
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "xx");
client.BaseAddress = new Uri(baseAddress);
}).AddPolicyHandler(PollyPolicys.xxx());
I've also tried to comment out that polly-policy, same behaviour
And I also added that CancelPendingRequests() but no luck
The TokenProvider has its own httpClient
client.BaseAddress is null here, and the baseaddress is set outside of this lambda.
The error message:
"This instance has already started one or more requests. Properties can only be modified before sending the first request"
And I simply request this client by:
var httpClient = _httpClientFactory.CreateClient("xxx");

Just to capture here the essence of our discussion in the chat:
The GetToken was also using the HttpClient
Changing the order of the BaseAddress assignment and Header assignment helped
The configureClient should be a synchronous delegate not an async one, because that is not supported by any of the AddHttpClient overload
The suggested modification is the following:
services.AddHttpClient();
var tokenData = await jwtTokenProvider.GetTokenAsync(appSettingsSectionIdentity);
if (tokenData is null || !tokenData.IsValid)
throw new TechnicalErrorException("Can't get a token xxx");
var token = await GetToken(tokenData, apiKey); //I assumed that this async as well
services.AddHttpClient("xxx", client =>
{
client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", token);
}).AddPolicyHandler(PollyPolicys.xxx());

Related

Exception when trying to use an existing instance of HttpClient in C#

Update- The below issue was solved by setting the proxy for
HttpClientHandler. But I still wonder how the first request succeeded
without setting the proxy ever? Why would only subsequent
requests(after the first request) would fail because of proxy?
I have a simple class to reuse a single instance of "HttpClient" across different requests. The issue I am facing is that only the first call succeeds, while all the subsequent call fails with a SocketException.
public class MyHttpClient
{
private static readonly HttpClient client;
static MyHttpClient()
{
client = new HttpClient();
client.BaseAddress = new Uri("http://x.com");
}
public async Task<string> GetSecurityToken(LoginData data)
{
var reqMsg = JsonConvert.SerializeObject(data);
var reqContent = new StringContent(reqMsg, System.Text.Encoding.UTF8, "application/json");
var response= await client.PostAsync("/api/v1/security/token", reqContent)
var result = await response.Content.ReadAsStringAsync();
return result
}
}
// The above class is consumed as
var client = new MyHttpClient();
var login = new LoginData("abc#gmail.com", "xxxx");
var result = await client.GetSecurityToken(login); // this call succeeds !
var result1 = await client.GetSecurityToken(login); // this call fails with an exception, but why?
The exception I get in second call is - SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

404 when accessing Design Automation API v3 through HttpClient

Running calls to the Design Automation API in Postman works just fine but when I try to make the same calls in C# using HttpClient they fail with a 404 that seems to actually hide an authentication error:
{
"developerMessage":"The requested resource does not exist.",
"userMessage":"",
"errorCode":"ERR-002",
"more info":"http://developer.api.autodesk.com/documentation/v1/errors/err-002"
}
That link leads to an authentication error:
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>1F52E60A45AEF429</RequestId>
<HostId>
[ Some base64 ]
</HostId>
</Error>
I'm following examples for how to use HttpClient, but I may be missing something. I successfully get the access token, run
var client = new HttpClient
{
BaseAddress = new Uri("https://developer.api.autodesk.com/da/us-east")
};
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue(TokenType, AccessToken);
then
var result = await client.GetAsync("/v3/forgeapps/me");
and the above json is the result's content. I use the same access token in Postman and it works.
I would wrap up the endpoint, headers, and httpmethod in the HttpRequestMessage. Then send it and assign it to HttpResponseMessage.
var client = new HttpClient
{
BaseAddress = new Uri("https://developer.api.autodesk.com/da/us-east/")
};
//throw the endpoint and HttpMethod here. Could also be HttpMethod.Post/Put/Delete (for your future reference)
var request = new HttpRequestMessage(HttpMethod.Get, "v3/forgeapps/me");
//also maybe try throwing the headers in with the request instead of the client
request.Headers.Add(TokenType, AccessToken);
// send the request, assign to response
HttpResponseMessage response = await client.SendAsync(request);
//then, we can grab the data through the Content
string result = await response.Content.ReadAsStringAsync();

Error Calling a Odata service in C#

I am trying to call a Odata service from the C# application. I have called the rest services before and consumed the responses in the C#, and trying Odata for the first time. Below is the Code I am using
using (var client = new HttpClient())
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
Uri uri = new Uri(BaseURL);
client.BaseAddress = uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var response = client.GetAsync(uri).Result;
var responsedata = await response.Content.ReadAsStringAsync();
I am using the same URL and the credentials in PostMan and it returns the response. But throws error i the code, is there something different we need to follow calling a Odata services.Please help with this
It is recommended to use a library to access OData. There are at least a couple of libraries that you can choose from, such as:
https://www.nuget.org/packages/Microsoft.OData.Client/ (OData v4)
https://www.nuget.org/packages/Microsoft.Data.OData/ (OData v1..3)

HttpClient access url with '#' (at symbol)

I'm integrating a service that returns a key when I a GET request to a URL that is in the following format:
https://username:password#service.com/refresh.key
When I access the URL in my browser, it returns the new key as expected, by when I do a GET request using HttpClient I get a 401.
HttpClient _client = new HttpClient();
var response = await _client.GetAsync(#"https://username:password#service.com/refresh.key"); // Returns a 401
I think it has something to do with the '#' in the URL, but I'm not sure how to fix it, I tried replacing it with '%40', but when I do that I get a UriFormatException.
Does anyone know how to do this?
You should modify Authorization header of HttpClient, can you try the code below;
HttpClient _client = new HttpClient();
byte[] usernamePasswordBytes = Encoding.ASCII.GetBytes("user:pass");
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(usernamePasswordBytes));
var response = await _client.GetAsync(#"https://service.com/refresh.key");
PS: Such username:pass#domain.com requests are BasicAuthentication request so in fact you try to make basic authentication request.
Hope this works for you
You don't need to provide credentials in url. Instead you can do:
using (var handler = new HttpClientHandler {Credentials = new NetworkCredential("username", "password")}) {
using (HttpClient _client = new HttpClient(handler)) {
var response = await _client.GetAsync(#"https://service.com/refresh.key");
}
}

Adding headers when using httpClient.GetAsync

I'm implementing an API made by other colleagues with Apiary.io, in a Windows Store app project.
They show this example of a method I have to implement:
var baseAddress = new Uri("https://private-a8014-xxxxxx.apiary-mock.com/");
using (var httpClient = new HttpClient{ BaseAddress = baseAddress })
{
using (var response = await httpClient.GetAsync("user/list{?organizationId}"))
{
string responseData = await response.Content.ReadAsStringAsync();
}
}
In this and some other methods, I need to have a header with a token that I get before.
Here's an image of Postman (chrome extension) with the header I'm talking about:
How do I add that Authorization header to the request?
A later answer, but because no one gave this solution...
If you do not want to set the header on the HttpClient instance by adding it to the DefaultRequestHeaders, you could set headers per request.
But you will be obliged to use the SendAsync() method.
This is the right solution if you want to reuse the HttpClient -- which is a good practice for
performance and port exhaustion problems
doing something thread-safe
not sending the same headers every time
Use it like this:
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Get, "https://your.site.com"))
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", your_token);
await httpClient.SendAsync(requestMessage);
}
When using GetAsync with the HttpClient you can add the authorization headers like so:
httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "Your Oauth token");
This does add the authorization header for the lifetime of the HttpClient so is useful if you are hitting one site where the authorization header doesn't change.
Here is an detailed SO answer
The accepted answer works but can get complicated when you want to try adding Accept headers. This is what I ended up with. It seems simpler to me, so I think I'll stick with it in the future:
client.DefaultRequestHeaders.Add("Accept", "application/*+xml;version=5.1");
client.DefaultRequestHeaders.Add("Authorization", "Basic " + authstring);
Sometimes, you only need this code.
httpClient.DefaultRequestHeaders.Add("token", token);
Following the greenhoorn's answer, you can use "Extensions" like this:
public static class HttpClientExtensions
{
public static HttpClient AddTokenToHeader(this HttpClient cl, string token)
{
//int timeoutSec = 90;
//cl.Timeout = new TimeSpan(0, 0, timeoutSec);
string contentType = "application/json";
cl.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));
cl.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", token));
var userAgent = "d-fens HttpClient";
cl.DefaultRequestHeaders.Add("User-Agent", userAgent);
return cl;
}
}
And use:
string _tokenUpdated = "TOKEN";
HttpClient _client;
_client.AddTokenToHeader(_tokenUpdated).GetAsync("/api/values")
These days, if you are using MS Dependency Injection, it's highly recomended to plug in the IHttpClientFactory:
builder.Services.AddHttpClient("GitHub", httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// using Microsoft.Net.Http.Headers;
// The GitHub API requires two headers.
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
});
var httpClient = _httpClientFactory.CreateClient("GitHub");
This way you avoid adding default request headers to a globally shared httpclient and moreover don't have to deal with manual creation of the HttpRequestMessage.
Source:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-6.0#consumption-patterns

Categories

Resources