I'm trying to write an integration test for a DelegatingHandler that prevents duplicate requests. The handler checks a database to see if the request is already being processed and returns a 407-Conflict if a duplicate request is made while the previous request is still running.
I have the following code in my test:
HttpClient client = new HttpClient();
var responseTask1 = client.PostAsJsonAsync(RequestUriWithDuplicatePrevention, ReadRequestContent("DuplicateRequestJsonContent.json"));
var responseTask2 = client.PostAsJsonAsync(RequestUriWithDuplicatePrevention, ReadRequestContent("DuplicateRequestJsonContent.json"));
var response1 = responseTask1.Result;
var response2 = responseTask2.Result;
Both requests are being logged into the database at the exact same time. How can I delay the second request for a period of time?
I've tried adding a Thread.Sleep(500) but it didn't seem to make a difference.
Revised Code
This code seems to work most of the time but it not 100% reliable.
[TestMethod]
public void ShouldReturn407ConflictWhenDuplicateRequestSubmitted()
{
var results = ExecutePostRequests().Result;
Assert.AreEqual(HttpStatusCode.OK, results[0].StatusCode);
Assert.AreEqual(HttpStatusCode.Conflict, results[1].StatusCode);
}
private async Task<HttpResponseMessage[]> ExecutePostRequests()
{
HttpClient client = new HttpClient();
var task1 = ExecutePost(client, 0);
var task2 = ExecutePost(client, 4000);
var response1 = await task1;
var response2 = await task2;
return new[] {response1, response2};
}
private async Task<HttpResponseMessage> ExecutePost(HttpClient client, int delay)
{
await Task.Delay(delay);
return await client.PostAsync(RequestUriWithDuplicatePrevention,
ReadRequestContent("DuplicateRequestJsonContent.json"));
}
The web service being executed has a Thread.Sleep(5000).
The specific problem in your original code is that it is sleeping between getting results, when you should be sleeping between starting the asynchronous operations.
It could be corrected like so:
var responseTask1 = client.PostAsJsonAsync(...);
Thread.Sleep(2000);
var responseTask2 = client.PostAsJsonAsync(...);
var response1 = responseTask1.Result;
var response2 = responseTask2.Result;
Your revised code does not suffer this issue and should work. Though, I would change this:
var response1 = await task1;
var response2 = await task2;
return new[] {response1, response2};
To a more efficient:
return await Task.WhenAll(task1, task2);
Related
It happens on Console and Windows Service apps, haven't tested on Windows Forms or WPF. When stepping thru the code whenever it hits an async call with await, the debug session exits abruptly, can't go further. As a workaround I'm using Task.Run() but changing all the async calls to use Task.Run() syntax during debug, and put it back to "await" syntax for deployment is a hassle.
Here in this method below, the following line is the issue during Debug, it works as expected when deployed.
var response = await httpClient.SendAsync(request);
Work around while debugging is to change the above line to this:
//var response = await httpClient.SendAsync(request);
var task = Task.Run(() => httpClient.SendAsync(request));
task.Wait();
var response = task.Result;
Code:
private static async Task<string> SendRequest()
{
string result = "";
string url = "http://localhost:5119/WeatherForecast";
var URL = new Uri(url);
var method = new HttpMethod("GET");
using (var request = new HttpRequestMessage(method, URL))
{
var response = await httpClient.SendAsync(request);
//var task = Task.Run(() => httpClient.SendAsync(request));
//task.Wait();
//var response = task.Result;
result = await response.Content.ReadAsStringAsync();
}
return result;
}
I am trying to send an Http Get message to the Google location Api which is supposed to have Json data such as this
https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt
as you noticed the response is in Json. I want to give a a Http Call to that URL and save the Json content in a variable or string. My code does not give out any errors but it is also not returning anything
public async System.Threading.Tasks.Task<ActionResult> GetRequest()
{
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
string data = response.Content.ToString();
return data;
}
I want to send out a Get Request using HttpClient() or anything that will send out the URL request and then save that content into a string variable . Any suggestions would be appreciated, again my code gives no errors but it is not returning anything.
Use ReadAsStringAsync to get the json response...
static void Main(string[] args)
{
HttpClient client = new HttpClient();
Task.Run(async () =>
{
HttpResponseMessage response = await client.GetAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
string responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
});
Console.ReadLine();
}
If you use response.Content.ToString() it is actually converting the datatype of the Content to string so you will get System.Net.Http.StreamContent
Try this.
var client = new HttpClient();
HttpResponseMessage httpResponse = await client.GetAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
string data = await httpResponse.Content.ReadAsStringAsync();
How about using the Json framework for .NET powered by Newtonsoft ?
You can try to parse your content to a string with this.
Well, your code can be simplified:
public async Task<ActionResult> GetRequest()
{
var client = new HttpClient();
return await client.GetStringAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
}
However...
My code does not give out any errors but it is also not returning anything
This is almost certainly due to using Result or Wait further up the call stack. Blocking on asynchronous code like that causes a deadlock that I explain in full on my blog. The short version is that there is an ASP.NET request context that only permits one thread at a time; await by default will capture the current context and resume in that context; and since Wait/Result is blocking a thread in the request context, the await cannot resume executing.
Try this
public async static Task<string> Something()
{
var http = new HttpClient();
var url = "https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt";
var response = await http.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<string>(result);
return result;
}
return "";
}
var result = Task.Run(() => Something()).Result;
The following code just hangs when called from an ASP.NET app:
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = await client.GetByteArrayAsync(sUrl);
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
If I change to following it works fine:
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = null;
var task = Task.Run(async () => { urlContents = await client.GetByteArrayAsync(strUrl); });
task.Wait();
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
Is the fact that method signature return is a Task<XPathNavigator> causing the issue? Thank you.
Somewhere higher up the call stack there is a .Wait() being performed on the task that is returned from UspsCreateAndPostRequest.
Because you wrapped the call inside a Task.Run you lost the execution context, that is why it works. Doing
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = await client.GetByteArrayAsync(sUrl).ConfigureAwait(false);
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
would achieve the same goal with less resources (but it would be even better to fix the wait higher up on the chain).
I make a httpclient postasync and I want to convert the response to my model object. This is my code:
var response = client.PostAsync(TERMBASED_ENDPOINT,
new StringContent(JsonConvert.SerializeObject(request).ToString(),
Encoding.UTF8, "application/json")).Result;
var result = await response.Content.ReadAsAsync<MyObject>();
//other code synchronously processed
So, this code is asynchronously processed. What is the best method to wait for the response to be processed and just after this happens to continue to run the synchronous code??
Thank you!
"await" your post call to unwrap the response.
var response = await client.PostAsync(TERMBASED_ENDPOINT,
new StringContent(JsonConvert.SerializeObject(request).ToString(),
Encoding.UTF8, "application/json"));
var result = JsonConvert.DeserializeObject<MyObject>(response.Content);
I think what you are wanting to do is store the Task in a variable, do some work, and then await the response.
var response = client.PostAsync(TERMBASED_ENDPOINT,
new StringContent(JsonConvert.SerializeObject(request).ToString(),
Encoding.UTF8, "application/json")).Result;
var readTask = response.Content.ReadAsAsync<MyObject>();
//other code synchronously processed
var result = await readTask;
Alternatively, if you have several asynchronous tasks you can wait for them all and then process the results.
var response = client.PostAsync(TERMBASED_ENDPOINT,
new StringContent(JsonConvert.SerializeObject(request).ToString(),
Encoding.UTF8, "application/json")).Result;
var readTask = response.Content.ReadAsAsync<MyObject>();
//other code synchronously processed
await Task.WhenAll(readTask);
var result = readTask.Result;
After considering this interesting answer HttpClient.GetAsync(...) never returns..., I still have a situation where my HttpClient is not returning when I use await (sample code below). In addition. I use this helper routine from both asp.net MVC5 Controllers (UI-driven) and the WebApi. What can I do to:
Use await instead of the (dreaded) .Result and still have this function return?
Reuse this same routine from both MVC Controllers and WebApi?
Apparently, I should replace the .Result with .ConfigureAwait(false) but this seems to contradict with the fact that "Task4" in the post cited above works fine with an await httpClient.GetAsync. Or do I need separate routines for Controller and WebApi cases?
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// Why does this never return when await is used?
HttpResponseMessage response = hclient.GetAsync("api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
UPDATE: My call chain, which starts with a Telerik Kendo Mvc.Grid DataBinding call is as follows:
[HttpPost]
public async Task<ActionResult> TopLicenseGrid_Read([DataSourceRequest]DataSourceRequest request)
{
var res = await GetLicenseInfo();
return Json(res.ToDataSourceResult(request)); // Kendo.Mvc.Extensions.DataSourceRequest
}
Then:
private async Task<IEnumerable<CsoPortalLicenseInfoModel>> GetLicenseInfo()
{
...
// Never returns
var qry = #"app_metadata.tc_app_user.country:""DE""";
return await TcSearchMultiUsersAsync(qry);
}
Then, the routine shown in full above but now WITHOUT the .Result:
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// This now returns fine
HttpResponseMessage response = hclient.GetAsync("api/v2/users?search_engine=v2&q=" + elasticQuery");
if (response.IsSuccessStatusCode)
{
// This returns my results fine too
var content = await response.Content.ReadAsStringAsync();
// The following line never returns results. When debugging, everything flows perfectly until I reach the following line, which never
// returns and the debugger returns me immediately to the top level HttpPost with a result of null.
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
You need to await everything. It's not enough that you await on one of them, you need to await on all of them:
This:
HttpResponseMessage response = hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
Should be:
HttpResponseMessage response = await hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2");
It is enough to have one blocking call to .Result in order to deadlock. You need "async all the way".