I have a "api" e.g. repository pattern, I wrote to return xml from the web and then hydrate it to classes.
It seems to hang on the result of the GetXmlAsync(url) method.
public async Task<string> GetXmlAsync(string url)
{
string xml = string.Empty;
HttpMessageHandler handler = new HttpClientHandler();
HttpClient httpClient = new HttpClient(handler);
Uri uri = new Uri(url, UriKind.Absolute);
HttpResponseMessage response = await httpClient.GetAsync(uri);
xml = await response.Content.ReadAsStringAsync();
return xml;
}
But when I use the same code in a unit test, it works.
In the app, I call it like so:
public async Task<IEnumerable<Post>> GetRecentAsync(int page)
{
string url = this.urls.GetRecent(page);
string xml = string.Empty;
var xmlTask = this.loader.GetXmlAsync(url);
xml = xmlTask.Result; // Hangs right here.
var results = this.modelLoader.XmlToPost(xml);
if (results.Count() < 1)
{
this.LastError = XmlLoadError;
}
return results.AsEnumerable();
}
[TestMethod]
public async Task Integration_HttpLoader_GetXmlAsync_GetRecent_Xml_ShouldNotBeNullOrEmpty()
{
int page = 1;
string xml = string.Empty;
IUrl url = this.GetUrlHelper();
ILoader loader = this.GetIntegrationLoader(false);
xml = await loader.GetXmlAsync(url.GetRecent(page));
Assert.IsTrue(!string.IsNullOrEmpty(xml));
}
In your app, you are not preceding the call to this.loader.GetXmlAsync(url) with await You hit this line, fire an async task on another thread then immediately proceed to the next line without having ever gotten the response. It works in your unit test because you correctly use the await keyword.
You are causing a deadlock by synchronously blocking on the result of the task.
Follow these best practices:
Do not block on async code (make it async all the way down).
e.g., var xml = await this.loader.GetXmlAsync(url); in GetRecentAsync.
Use ConfigureAwait(false) in your "library" async methods if they can continue on a thread pool thread.
e.g., var response = await httpClient.GetAsync(uri).ConfigureAwait(false); and xml = await response.Content.ReadAsStringAsync().ConfigureAwait(false); in GetXmlAsync.
Related
I'm successfully getting a token back from my GetAccessToken() and GetAccessTokenAsync methods, but the token isn't retrieved until after the main method of GetCourses, which won't work because that's the method that collects the data I need to show on my cshtml page. I've tried pulling apart this controller and creating a Globals class that will house just the URIs, apiKey, and token, but then I read that's bad practice for MVC so I ditched that effort. It was getting called after the GetCourses method anyway, so it was dead end too.
I'm newer to MVC and come from a WebForms background where I was used to being able to throw this kind of code in my PageInit, but am struggling to figure out how to pull this off in MVC. Can someone help me figure out what I am doing wrong or if I need to go about this a different way?
public ActionResult GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = GetAccessToken().ToString();
IEnumerable<Course> courses = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri((string)TempData["EthosURI"]);
client.DefaultRequestHeaders.Add("Authorization", "Bearer {" + (string)TempData["Token"] + "}");
client.DefaultRequestHeaders.Add("Accept", "application/json");
//HTTP GET
var responseTask = client.GetAsync("courses");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
}
else //web api sent error response
{
//log response status here..
courses = Enumerable.Empty<Course>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(courses);
}
public static async Task<string> GetAccessToken()
{
var token = await GetAccessTokenAsync("redactedUrl", "redactedAPIKey");
return token;
}
public static async Task<string> GetAccessTokenAsync(string ethosURI, string apiKey)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ethosURI);
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(ethosURI)
};
request.Headers.Clear();
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("Accept", "application/json");
request.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true };
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
The (non-blocking) way in C# to wait for a task to complete is to use the await keyword. And for a method to use the await keyword, it has to be marked async. By using await, you not only wait for the task to complete, but also the current thread is not blocked. Wrapping an asynchronous operation in another method would not make it synchronous. In other words, the asynchronous nature propagates up the call hierarchy and the caller has to await. So, the GetAccessToken() still has to be awaited. A controller action can be marked asynchronous as well, so you probably want:
public async Task<ActionResult> GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = (await GetAccessToken()).ToString(); // note the additional parentheses
....
Note the additional parantheses above before calling ToString(). However, since GetAccessToken() already returns a string, you don't need the redundant ToString() call:
TempData["Token"] = await GetAccessToken();
Now, you can also change this:
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
to just:
courses = await result.Content.ReadAsAsync<IList<Course>>();
Microsoft has quite good documentation on asynchronous programming and I would recommend checking it out.
That's not how async works in C#. You need either to make GetCourses() async AND await for GetAccessToken(), or use dirty hack GetAccessToken().GetAwaiter().GetResult() but it may become not safe in certain circumstances.
I am calling a method of which returns Task, in the calling method I need to read the response in form of string.
Here's the code that I have put:
static public async Task<HttpResponseMessage> Validate(string baseUri,HttpContent content) {
HttpResponseMessage response = new HttpResponseMessage();
response = await client.PostAsync(baseUri,content);
return response;
}
public string test(){
string postJson = "{Login = \"user\", Password = \"pwd\"}";
HttpContent stringContent = new StringContent(postJson,
UnicodeEncoding.UTF8, "application/json");
HttpResponseMessage result=Validate(Uri,stringContent);
var json = result.Content.ReadAsStringAsync().Result;
}
I expect string but this error is thrown:
Cannot implicitly convert type
'System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>' to
'System.Net.Http.HttpResponseMessage'
Your problem is at:
HttpResponseMessage result=Validate(Uri,stringContent);
Validate is returning a task, not a HttpResponseMessage.
Change that to:
var result = Validate(Uri,stringContent).Result;
Just to clean up the code a bit and avoid using Result if you can afford to change it like:
static public async Task<HttpResponseMessage> Validate(string baseUri, HttpContent content)
{
return await client.PostAsync(baseUri,content);
}
public async Task<string> test()
{
var postJson = "{Login = \"user\", Password = \"pwd\"}";
var stringContent = new StringContent(postJson, UnicodeEncoding.UTF8, "application/json");
var result = await Validate(Uri,stringContent);
return await result.Content.ReadAsStringAsync();
}
Be consistent with your coding style. As to why calling .Result is bad, as people are pointing out in the comments, check this blog.
Method Validate returns the task and is asynchronous. Basically you can call this method in two different ways:
1) await the result of Validate in another async method, like this...
public async Task<string> test() {
string postJson = "{Login = \"user\", Password = \"pwd\"}";
HttpContent stringContent = new StringContent(postJson,
UnicodeEncoding.UTF8, "application/json");
HttpResponseMessage result = await Validate(Uri,stringContent);
var json = result.Content.ReadAsStringAsync().Result;
}
2) block current execution while waiting for this resul, like this...
public string test() {
string postJson = "{Login = \"user\", Password = \"pwd\"}";
HttpContent stringContent = new StringContent(postJson,
UnicodeEncoding.UTF8, "application/json");
HttpResponseMessage result = Validate(Uri,stringContent).Result;
var json = result.Content.ReadAsStringAsync().Result;
}
Choose the 1st way by default unless you really have an urge to block the calling thread.
Note that when using 2nd solution in WinForms, WPF or ASP.NET apps you will probably have a deadlock. modifying Validate method like this should solve the problem:
response = await client.PostAsync(baseUri,content).ConfigureAwait(false);
Just for readability change the method name from Validate(..) to ValidateAsync(..). Just to make clear that this method returns a task and you have to await it.
You're missing an await when calling your Validate method. If you do not want or can make the caller async then use the method GetAwaiter().GetResult() and you'll get what you want.
Replace ReadAsStringAsync().Result with ReadAsStringAsync().GetAwaiter().GetResult()
Like others wrote in comments calling .Result is bad because it could cause locks. The same applies in general to GetAwaiter().GetResult() but calling them is preferred over calling Result. The best option is to use await/async.
When you want to know whats the difference between GetAwaiter().GetResult() vs calling Result you can read this:
Is Task.Result the same as .GetAwaiter.GetResult()?
You can easily write:
var json = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
I am working on a Windows Phone application. While invoking the below function, after executing this line:
HttpResponseMessage response = await client.GetAsync(connection1).ConfigureAwait(false);
it skips the rest of the code and control goes to the parent function and execute the rest of the code there and come back to this function again. How to fix this problem?
public async void vehicleValidation()
{
//isValidVehicle = true;
var client = new System.Net.Http.HttpClient();
//string connection = "http://mymlcp.co.in/mlcpapp/get_slot.php?vehiclenumber=KL07BQ973";
string connection1 = string.Format("http://mymlcp.co.in/mlcpapp/?tag=GetIsValidUser&employeeId={0}&name={1}",txtVeh.Text,"abc");
HttpResponseMessage response = await client.GetAsync(connection1).ConfigureAwait(false);
//var response = await client.GetAsync(connection1);
// HttpResponseMessage response = client.GetAsync(connection1).Result;
var cont = await response.Content.ReadAsStringAsync();
var floorObj = Newtonsoft.Json.Linq.JObject.Parse(cont);
//var resp = await (new MLCPClient()).GetIsValidUser(txtVeh.Text, "xyz");
if (String.IsNullOrEmpty(floorObj["error"].ToString()) || floorObj["error"].ToString().Equals("true"))
{
isValidVehicle = false;
}
else
{
isValidVehicle = true;
}
}
You should never have async void unless you are writing a event handler, you need to make your function return a Task and then await the function in your parent function.
Read "Async/Await - Best Practices in Asynchronous Programming" for a introduction on the best practices like never doing async void and making your code "async all the way"
I am calling a POST in an action method using HttpClient, but I am not sure if it is being done correctly. I dont' need it to be async. Basically, if a user is created on my system successfully, I create them in another system. Here is the code I am calling:
using (var client = new HttpClient())
{
var postData = new FormCollection();
postData["api_token"] = ConfigurationManager.AppSettings["ApiToken"];
postData["api_action"] = "Save";
postData["customer_email"] = userName;
postData["customer_password"] = password;
var result = client.PostAsync(string.Format("{0}/api", ConfigurationManager.AppSettings["Url"]), content).Result;
var xmlResponse = result.Content.ReadAsStringAsync().Result;
}
The following two lines:
var result = client.PostAsync(string.Format("{0}/api", ConfigurationManager.AppSettings["Url"]), content).Result;
var xmlResponse = result.Content.ReadAsStringAsync().Result;
should be refactored to the following:
var result = await client.PostAsync(string.Format("{0}/api", ConfigurationManager.AppSettings["Url"]), content);
var xmlResponse = await result.Content.ReadAsStringAsync();
You have to use the await, because otherwise you block your thread calling the Result at the end of the asynchronous methods. Furthermore, you shoulnd't forget that corresponding action should return a Task<ActionResult>.
public async Task<ActionResult> ActionName()
I previously posted a question about using HTTPClient with async/await. Now I'm trying to figure out how to do this in such a way as to actually make the Post calls execute at the same time while still being able to handle the resulting HttpResponseMessage.
This is what I've come up with. However, being a noob to await/async, I'm still unsure about if I'm doing this correctly or not. Could someone verify that this is the proper way to do this ... or at least a proper way.
public async Task ProcessAsync() {
//Query lists
List<Member> list = dbContext.Users.Where(u => u.IsMember).ToList();
//Add members to mailing list through web service
await AddMembersAsync(list);
}
private async Task AddMembersAsync(List<Member> members) {
using(var _client = new HttpClient()) {
//Initialize Http Client
...
var responses = await Task.WhenAll(members.Select(x => PostMemberAsync(x,_client)));
await Task.WhenAll(responses.Select(r => ProcessResponseAsync(r,client)));
}
}
private async Task<HttpResponseMessage> PostMemberAsync(Member member, HttpClient client) {
var jss = new JavaScriptSerializer();
var content = jss.Serialize(new MemberPost() {
email_address = member.email,
...
});
return await client.PostAsync("uri",new StringContent(content, Encoding.UTF8, "application/json"));
}
private async Task ProcessResponsesAsync(HttpResponseMessage response, HttpClient client) {
if(response.IsSuccessStatusCode) {
var responseText = await response.Content.ReadAsStringAsync();
var jss = new JavaScriptSerializer();
var userid = jss.Deserialize<MemberResponse>(responseText);
//Store mailing user's id
...
}
response.Dispose();
}
This appears to me like it would be correct. However, I have a slight problem with this. I need to tie each HttpResponseMessage to the member for which the message was created. My current code only returns Task but the response message does not contain a link to the user. (The service I'm posting to returns an id specific to the service. I need to keep track of that Id for each user so that I have a link between the member id and the service id).
Does my requirement of linking the id from the response message to the member make it unrealistic to use the above code or is there a way to somehow return the member as part of the task results?
I'm suggesting this without trying it out so please be careful but I would replace these two lines:
var responses = await Task.WhenAll(members.Select(x => PostMemberAsync(x,_client)));
await Task.WhenAll(responses.Select(r => ProcessResponseAsync(r,client)));
with this:
await Task.WhenAll(members.Select(async x =>
{
var response = await PostMemberAsync(x, _client);
await ProcessResponseAsync(response, client, x);
}));
And of course you need to enhance ProcessResponseAsync by the argument Member
I need to tie each HttpResponseMessage to the member for which the message was created.
When doing asynchronous programming, I find it useful to avoid side effects. In other words, if you have a method that calculates or determines something, return that value from the method rather than saving it in some member variable.
private async Task<MemberResponse> ProcessResponseAsync(HttpResponseMessage response, HttpClient client)
{
using (response)
{
if(response.IsSuccessStatusCode)
{
var responseText = await response.Content.ReadAsStringAsync();
var jss = new JavaScriptSerializer();
var userid = jss.Deserialize<MemberResponse>(responseText);
return userid;
}
else
{ ... }
}
}
Add a small helper method, and your calling code becomes quite clean:
private async Task<HttpResponseMessage> ProcessMemberAsync(Member member, HttpClient client)
{
var response = await PostMemberAsync(member, client);
return await ProcessResponseAsync(response, client);
}
private async Task AddMembersAsync(List<Member> members)
{
using(var client = new HttpClient())
{
... // Initialize HttpClient
var responses = await Task.WhenAll(members.Select(x => ProcessMemberAsync(x, client)));
for (int i = 0; i != members.Count; ++i)
{
var member = members[i];
var response = responses[i];
...
}
}
}