I have a method to POST some data to server. Server may return aswer means I need change URL and send data to other server. I use for it HttpClient class from 4.5 framework. I use either do-while loop to repeat requests until I do not need redirecton. But there is a problem.
The question is why if I create HttpClient instance outside loop, second await didn't happen and my programm get out of loop, but if I create HttpClient instance inside the loop again and again - all is fine? Can I reuse one HttpClient for several POST requsts inside do-while loop inside async method?
My working code sample:
public async Task<bool> GameLogin()
{
JToken r;
do
{
var clientHandler = new HttpClientHandler
{
CookieContainer = this.myCContainer,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
};
var client = new HttpClient(clientHandler);
client.DefaultRequestHeaders.Add("client-ver", Versoin);
client.DefaultRequestHeaders.Add("method", "SignIn");
client.DefaultRequestHeaders.Add("authKey", AppParams["auth_key"].ToString());
var content = new StringContent(this.SendStr);
var answer = await client.PostAsync(this.CurrentUrl, content);
var rawString = await answer.Content.ReadAsStringAsync();
DinamicData = JObject.Parse(rawString);
r = DinamicData["r"];
if (r == null) continue;
this.CurrentUrl = string.Format("http://{0}/main.ashx", r);
} while (r != null);
return DinamicData.Type != JTokenType.Null;
}
My did not working code sample:
public async Task<bool> GameLogin()
{
var clientHandler = new HttpClientHandler
{
CookieContainer = this.myCContainer,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
};
var client = new HttpClient(clientHandler);
client.DefaultRequestHeaders.Add("client-ver", Versoin);
client.DefaultRequestHeaders.Add("method", "SignIn");
client.DefaultRequestHeaders.Add("authKey", AppParams["auth_key"].ToString());
var content = new StringContent(this.SendStr);
JToken r;
do
{
var answer = await client.PostAsync(this.CurrentUrl, content);
var rawString = await answer.Content.ReadAsStringAsync();
DinamicData = JObject.Parse(rawString);
r = DinamicData["r"];
if (r == null) continue;
this.CurrentUrl = string.Format("http://{0}/main.ashx", r);
} while (r != null);
return DinamicData.Type != JTokenType.Null;
}
And my second code did not wait await in secont loop turn.
Okay, now we've got the exception...
I suspect the problem isn't reusing the HttpClient - it's reusing this:
var content = new StringContent(this.SendStr);
I suspect that content is probably being disposed by the HttpClient's first call, which makes the second call fail.
Just move that line inside the loop (it's inside the loop in your working code), and I expect all will be well.
Related
I'm creating a Task in C# but I'm not sure what I do is correct. I'm using Restsharp and in Restsharp there are two methods: Execute and ExecuteAsync. I want to do an Async call but I also need to return data to the client without blocking the execution.
Therefore I created a task which will use Execute instead of ExecuteAsync. The reason why is because I have to wait until I get a response back and then return it in the right data structure. So I thought there is no use in using ExecuteAsync if I have to await it in a Task...
My code looks as follows:
public Task<Response> ExecuteAsync()
{
return new Task<Response>(() =>
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = client.Execute(request);
return new Response()
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
});
}
Is this correct? The client should be able to call ExecuteAsync without blocking the execution.
I strongly suspect you should really just use ExecuteAsync and write an async method:
public async Task<Response> ExecuteAsync()
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = await client.ExecuteAsync(request).ConfigureAwait(false);
return new Response
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
}
I am using a library for communicate with a crypto api.
In Console Application every thing is ok about async post.
But in webforms it goes to sleep and nothing happens!
Just loading mode.
Here is the method :
private async Task<WebCallResult<T>> PostAsync<T>(string url, object obj = null, CancellationToken cancellationToken = default(CancellationToken))
{
using (var client = GetHttpClient())
{
var data = JsonConvert.SerializeObject(obj ?? new object());
var response = await client.PostAsync($"{url}", new StringContent(data, Encoding.UTF8, "application/json"), cancellationToken);
var content = await response.Content.ReadAsStringAsync();
// Return
return this.EvaluateResponse<T>(response, content);
}
}
private HttpClient GetHttpClient()
{
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
var client = new HttpClient(handler);
client.BaseAddress = new Uri(this.EndpointUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
In line var response = web site goes to loading mode.
No error - Nothing - Just Loading.
How can i fix this problem in webforms?
In Console Application it is working fine.
Maybe i should change something in Web.config!!!
Here is the library
I want to send a http post that can take a couple of seconds to reply without freezing my UI, currently this code just hangs my application when the method is callled.
What am I doing wrong and how do i achieve my goal?
private async Task<string> DoHttpClientPost(string method, IDictionary<string, object> args = null)
{
{
HttpClientHandler handler = new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
handler.Proxy = null;
HttpResponseMessage response;
using (var myHttpClient = new HttpClient(handler))
{
myHttpClient.DefaultRequestHeaders.ExpectContinue = false;
myHttpClient.DefaultRequestHeaders.Add("Accept-Charset", "ISO-8859-1,utf-8");
myHttpClient.DefaultRequestHeaders.Add(APPKEY_HEADER, CustomHeaders.GetValues(APPKEY_HEADER));
myHttpClient.DefaultRequestHeaders.Add(SESSION_TOKEN_HEADER, CustomHeaders.GetValues(SESSION_TOKEN_HEADER));
myHttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json-rpc"));
var call = new JsonRequest { Method = method, Id = 1, Params = args };
var jsonObject = JsonConvert.Serialize<JsonRequest>(call);
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json-rpc");
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
}
Console.WriteLine("\nCalling: " + method + " With args: " + JsonConvert.Serialize<IDictionary<string, object>>(args));
string jsonResponse = await response.Content.ReadAsStringAsync();
return jsonResponse;
}
}
public T Invoke<T>(string method, IDictionary<string, object> args = null)
{
if (method == null)
throw new ArgumentNullException("method");
if (method.Length == 0)
throw new ArgumentException(null, "method");
var jsonString = DoHttpClientPost(method, args).Result;
var jsonResult = JsonConvert.Deserialize<JsonResponse<T>>(jsonString);
return jsonResult.Result;
}
var jsonString = DoHttpClientPost(method, args).Result;
This is your culprit. If you call .Result on a Task from the UI thread it will hang.
You'll need to async all the way up - so Invoke should be async and return a Task<T> and await the DoHttpClientPost call, the caller should be async etc. etc. etc.
You have to make two changes
Modify this line from
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
to
response = await myHttpClient.PostAsync(new Uri(EndPoint), content).ConfigureAwait(false);
And looks like your intention is to wait for the post call to complete and return the results, so modify this line from
var jsonString = DoHttpClientPost(method, args).Result;
to
var jsonStringTask = DoHttpClientPost(method, args);
jsonStringTask.Wait(); //wait for http post call to complete.
var jsonString = jsonStringTask.Result;
I'm working with Windows Phone Runtime API.
I declare a timer, which every 2 seconds does async http connection in Listen method.
Timer t = new Timer(Listen, null, 0, 2000);
Listen method:
private async void Listen(object state)
{
string url = "http://www.mywebpage.com?data=my_data";
string responseBodyAsText = null;
var response = new HttpResponseMessage();
var httpClient = new HttpClient();
try
{
response = await httpClient.GetAsync(new Uri(url));
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
catch
{
//...
}
Debug.WriteLine(responseBodyAsText);
httpClient.Dispose();
}
My problem is that responseBodyAsText contains always the same data (given the same uri) and not as I would expect different data according to my external actions (modifying web page or different results with the same uri).
Does HttpClient remembers content during liftime of application? How can I solve this problem?
HttpClient does have caching on by default. You can turn it off by passing it an HttpBaseProtocolFilter:
var filter = new HttpBaseProtocolFilter
{
CacheControl.ReadBehavior = HttpCacheReadBehavior.MostRecent,
CacheControl.WriteBehavior = HttpCacheWriteBehavior.NoCache;
}
Side note: You could also, instead of a Timer, use Task.Delay to achieve the timer behavior (it internally uses one):
private async Task ListenAsync()
{
while (someCondition)
{
string url = "http://www.mywebpage.com?data=my_data";
string responseBodyAsText = null;
var response = new HttpResponseMessage();
using (var httpClient = new HttpClient())
{
try
{
response = await httpClient.GetAsync(new Uri(url));
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
catch
{
//...
}
Debug.WriteLine(responseBodyAsText);
await Task.Delay(2000);
}
}
I'm having an issue with HttpClient and async requests. Basically i'm having an async method that is creating async requests with a shared HttpClient that is initialized in the ctor.
My problem is that it seems that the HttpClient blocks when calling my method in an async manner.
Here is my calling code:
var tasks = trips.Select(u => api.Animals.GetAsync(u * 100, 100).ContinueWith(t =>
{
lock (animals)
{
if (t.Result != null)
{
foreach (var a in t.Result)
{
animals.Add(a);
}
}
}
}));
await Task.WhenAll(tasks);
Here is the method that blocks with a shared HttpClient:
//HttpClient blocks on each request
var uri = String.Format("animals?take={0}&from={1}", take, from);
var resourceSegmentUri = new Uri(uri, UriKind.Relative);
var response = await _client.GetAsync(resourceSegmentUri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var animals = JsonConvert.DeserializeObject<T>(content);
return animals;
}
This snippet does not block, when using a client for each request:
using (var client = new HttpClient(){BaseAddress = new Uri(_config.BaseUrl)})
{
var uri = String.Format("animals?take={0}&from={1}", take, from);
var resourceSegmentUri = new Uri(uri, UriKind.Relative);
var response = await client.GetAsync(resourceSegmentUri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var animals = JsonConvert.DeserializeObject<T>(content);
return animals;
}
}
Is a shared HttpClient a no go? Or can I utilize it in some other way?
Using a shared HttpClient is actually recommended.
See my answer why - What is the overhead of creating a new HttpClient per call in a WebAPI client?