I have a functioning async Task that calls a web service:
private async Task GetResult()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Username", _username);
client.DefaultRequestHeaders.Add("Action", "GET");
/* etc */
var response = await client.GetAsync(client.BaseAddress);
}
}
I would like to separate out the creation of the HttpClient object so it can be parameterized and reused:
private async Task GetResult()
{
using (var client = GetClient(_baseAddress, _username))
{
var response = await client.GetAsync(client.BaseAddress);
}
}
private static HttpClient GetClient(string Address, string Username)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(Address);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Username", Username);
client.DefaultRequestHeaders.Add("Action", "GET");
/* etc */
return client;
}
}
While this appears functionally identical to me, the latter throws an AggregateException error with inner exception
Cannot Access a disposed object. Object name: 'System.Net.Http.HttpClient'.
Perhaps there is some async subtlety that I don't understand?
Get rid of the using inside of GetClient. You only use using for things that remain "in your ownership", you are "giving up ownership to the caller" when you return client;.
It is now the caller's resposability to use a using statement (which you do already correctly do in GetResult).
This has nothing to do with asnyc and is simply standard IDisposable behavior.
Related
I am looking to optimize my codeā¦
I have number of API with early same structure for client and handler.
But I have some questions about disposing.
I have read the using statement automatically dispose resources (here HttpClient and HttpClientHandler).
Could I rewrite my code here:
public static async Task<IEnumerable<T>> deletePostsAsync<T>(IEnumerable<string> urls) where T : BaseReturnValues
{
var httpClientHandler = new HttpClientHandler
{
Proxy = new WebProxy(proxy, true),
UseProxy = IsProxySelected
};
using (var client = new HttpClient(httpClientHandler))
{
client.BaseAddress = new Uri(URI);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(Head.key, Head.apikey);
var tasks = urls.Select(async url => await DeleteAsync<T>(client, url).ConfigureAwait(false));
var result = await Task.WhenAll(tasks).ConfigureAwait(false);
return result!;
}
async Task<U> DeleteAsync<U>(HttpClient client, string url) where U : BaseReturnValues
{
var statusCode = -1;
var json = "_";
var isSuccess = false;
try
{
using (HttpResponseMessage response = await client.DeleteAsync(url).ConfigureAwait(false))
{
statusCode = (Int32)response.StatusCode;
json = await response.Content.ReadAsStringAsync();
isSuccess = response.IsSuccessStatusCode;
}
}
catch (Exception ex)
{
//something to catch
}
:
return record;
}
}
To this piece of code without problem? Disposing resource is always done?
public static async Task<IEnumerable<T>> deletePostsAsync<T>(IEnumerable<string> urls) where T : BaseReturnValues
{
using (var client = SetClientSettings())
{
var tasks = urls.Select(async url => await DeleteAsync<T>(client, url).ConfigureAwait(false));
var result = await Task.WhenAll(tasks).ConfigureAwait(false);
return result!;
}
async Task<U> DeleteAsync<U>(HttpClient client, string url) where U : BaseReturnValues
{
:
:
return record;
}
}
public static Httpclient SetClientSettings()
{
var httpClientHandler = new HttpClientHandler
{
Proxy = new WebProxy(proxy, true),
UseProxy = IsProxySelected
};
var client = new HttpClient(httpClientHandler);
client.BaseAddress = new Uri(URI);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(Head.key, Head.apikey);
return client;
}
So I have created a method SetClientSettings and this method create the client, the clienthandler, add some headers to client and return client.
so
var httpClientHandler = new HttpClientHandler
{
Proxy = new WebProxy(proxy, true),
UseProxy = IsProxySelected
};
using (var client = new HttpClient(httpClientHandler))
{
client.BaseAddress = new Uri(URI);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(Head.key, Head.apikey);
var tasks = urls.Select(async url => await DeleteAsync<T>(client, url).ConfigureAwait(false));
var result = await Task.WhenAll(tasks).ConfigureAwait(false);
return result!;
}
is really equivalent to??:
using (var client = SetClientSettings())
{
var tasks = urls.Select(async url => await DeleteAsync<T>(client, url).ConfigureAwait(false));
var result = await Task.WhenAll(tasks).ConfigureAwait(false);
return result!;
}
Your two pieces of code are not equivalent. There is a small difference, that with the first snippet you will dispose the client even when the following lines (e.g. client.BaseAddress = new Uri(URI); or client.DefaultRequestHeaders.Clear();) fail. Which is better. To achieve this with SetClientSettings you would need to wrap everything into try except and .Dispose() on exception.
I have read the using statement automatically dispose resources (here HttpClient and HttpClientHandler).
The using statement turns this:
using (var instance = something)
{
// body
}
into this
var instance = something;
try
{
// body
}
finally
{
if (instance != null)
{
instance.Dispose();
}
}
That's all it does. It is a syntactic sugar.
And so in your particular case the using statement will ensure that .Dispose() is called on HttpClient, regardless of whether exception is thrown or not.
Now, do we have to dispose HttpClient? Well, they say we have to, there's no reason not to believe it. In reality the HttpClient holds sockets under the hood, which have to be closed manually when done with. And so, yes, you should always dispose HttpClient when done with.
That being said, the best thing you can do is to have a singleton HttpClient for the duration of your app, and reuse it. You can tweak it to your needs (e.g. configure it to use pooled connections) for maximal efficiency. In such scenario you don't dispose it at all.
Note: you don't have to worry about disposing HttpClientHandler. By default HttpClient will dispose it when it is disposed itself. This behaviour can be modified by using different constructor.
Yes, the code you wrote is equivalent to each other. There's an option in HttpClient contructor to not to dispose message handler - by default, it's set to true. Anyway, as already suggested, you don't have to dispose HTTP client at all. There are reasons for that.
There's alot of nice articles about best practices of using HttpClient.
Try to search for IHttpClientFactory.
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I want to call an async Web Service method from inside an async method and user a WPF Window in order to ask the user for the username and Password.
The Code Looks similar to this:
private async Task GetUsers()
{
List<User> users = new List<User>();
using (var client = new HttpClient())
{
var authenticationWindow = new AuthenticateWindow();
authenticationWindow.ShowDialog();
//... Code to get the token ...
var token = "myToken";
client.BaseAddress = new Uri("http://localhost:56057/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
var response = await client.GetAsync("api/users/");
if (response.IsSuccessStatusCode)
{
users = await response.Content.ReadAsAsync<List<User>>();
}
else if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new UnauthorizedAccessException();
}
}
foreach(var user in users)
{
}
}
If I execute this Code I get the following exception:
An unhandled exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll
This happens when calling client.GetAsync("api/users/"). It has probably something to do with context switching happenening because I opened the window.
How can I solve this Problem?
The call stack just Shows "External Code".
If I remove the following two lines everything works fine:
var authenticationWindow = new AuthenticateWindow();
authenticationWindow.ShowDialog();
You have a UI element being instantiated and used within the scope of a service, try to instead separate these two from each-other. It sounds like you need to have this logic of requesting users in the AuthenticateWindow, perhaps on its Loaded event:
// Safe UI entry point for async work, i.e.; event handler.
async void OnLoaded(object sender, RoutedEventArgs e)
{
var users = await GetUsersAsync();
// Take action on the user's from the API.
}
And then have the GetUsers (renamed more appropriately to GetUsersAsync) focus only on consuming the HttpClient and corresponding API, materializing and returning the List<User> like so:
async Task<List<User>> GetUsersAsync()
{
List<User> users = new List<User>();
using (var client = new HttpClient())
{
//... Code to get the token ...
var token = "myToken";
client.BaseAddress = new Uri("http://localhost:56057/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
var response = await client.GetAsync("api/users/");
if (response.IsSuccessStatusCode)
{
users = await response.Content.ReadAsAsync<List<User>>();
}
else if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new UnauthorizedAccessException();
}
}
return users;
}
I'm using HttpClient in .net 4.5 application to post custom object to Web API service.
Code:
public async Task<HttpStatusCode> Run(Customer customer)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://someaddress.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync("api/TestApi", customer)
}
}
Is there a way to get size of this request before calling PostAsJsonAsync?
Thanks.
This is the first time i've tried making a call to an API and I'm struggling a little. I keep getting back my error message, I plan on using the json response to populate my object. The OMDB api instructions are here (not helpful though): http://www.omdbapi.com/
private static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://www.omdbapi.com/?");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("t=Captain+Phillips&r=json").Result;
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Error with feed");
}
}
}
You have placed the question mark (?) on the wrong place. Try like this:
private static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://www.omdbapi.com");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("?t=Captain+Phillips&r=json");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Error with feed");
}
}
}
Notice that the question mark is here:
HttpResponseMessage response = await client.GetAsync("?t=Captain+Phillips&r=json");
and not on the base url as you placed it.
Also in order to properly write your asynchronous method you need to await on it inside and not be eagerly calling the .Result property which of course is a blocking operation.
I use codes below to send POST request to a server:
string url = "http://myserver/method?param1=1¶m2=2"
HttpClientHandler handler = new HttpClientHandler();
HttpClient httpClient = new HttpClient(handler);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
HttpResponseMessage response = await httpClient.SendAsync(request);
I don't have access to the server to debug but I want to know, is this request sent as POST or GET?
If it is GET, How can I change my code to send param1 & param2 as POST data (not in the URL)?
A cleaner alternative would be to use a Dictionary to handle parameters. They are key-value pairs after all.
private static readonly HttpClient httpclient;
static MyClassName()
{
// HttpClient is intended to be instantiated once and re-used throughout the life of an application.
// Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads.
// This will result in SocketException errors.
// https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netframework-4.7.1
httpclient = new HttpClient();
}
var url = "http://myserver/method";
var parameters = new Dictionary<string, string> { { "param1", "1" }, { "param2", "2" } };
var encodedContent = new FormUrlEncodedContent (parameters);
var response = await httpclient.PostAsync (url, encodedContent).ConfigureAwait (false);
if (response.StatusCode == HttpStatusCode.OK) {
// Do something with response. Example get content:
// var responseContent = await response.Content.ReadAsStringAsync ().ConfigureAwait (false);
}
Also dont forget to Dispose() httpclient, if you dont use the keyword using
As stated in the Remarks section of the HttpClient class in the Microsoft docs, HttpClient should be instantiated once and re-used.
Edit:
You may want to look into response.EnsureSuccessStatusCode(); instead of if (response.StatusCode == HttpStatusCode.OK).
You may want to keep your httpclient and dont Dispose() it. See: Do HttpClient and HttpClientHandler have to be disposed?
Edit:
Do not worry about using .ConfigureAwait(false) in .NET Core. For more details look at https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html
This is how I use it for DI:
using HttpClient httpClient = clientFactory.CreateClient("name set in builder host");
// httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Token {token}");
HttpResponseMessage? res = await httpClient!.PostAsync(url, content);
try
{
res.EnsureSuccessStatusCode();
return res;
}
catch (Exception)
{
// Add error handling
}
content is:
List<KeyValuePair<string, string>> values = new()
{
new KeyValuePair<string, string>("data", "value")
};
FormUrlEncodedContent requestContent = new(values);
and clientFactory is the interface:
IHttpClientFactory
msdn interface
As Ben said, you are POSTing your request ( HttpMethod.Post specified in your code )
The querystring (get) parameters included in your url probably will not do anything.
Try this:
string url = "http://myserver/method";
string content = "param1=1¶m2=2";
HttpClientHandler handler = new HttpClientHandler();
HttpClient httpClient = new HttpClient(handler);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
HttpResponseMessage response = await httpClient.SendAsync(request,content);
HTH,
bovako