Please tell me why it works only once?
I want to create a button to update the time, but my app only shows me the first time I got
public async void q()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://cleverapps.ru/wp.php");
string getResponsestring = await response.Content.ReadAsStringAsync();
MessageBox.Show(getResponsestring);
}
private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
q();
}
This probably happens because caching is enabled by default.
Try adding the following line before making the GetAsync call:
client.DefaultRequestHeaders.IfModifiedSince = DateTime.UtcNow;
Related
I know this is not using the proper form, but can someone tell me why the first code snippet works and the 2nd causes a deadlock?
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = DownloadStringV3("https://www.google.com");
}
public string DownloadStringV3(String url)
{
var resp = new HttpClient().GetAsync(url).Result;
return resp.Content.ReadAsStringAsync().Result;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = DownloadStringV3("https://www.google.com").Result;
}
public async Task<string> DownloadStringV3(String url)
{
var resp = await new HttpClient().GetAsync(url);
return await resp.Content.ReadAsStringAsync();
}
Different synchronization contexts? This is using .NET 4.8
Every time you await something, you say, I am temporary done here, give some other thread a chance to execute and then continue from here, when the async task is completed.
So in your case await new HttpClient().GetAsync(url) switches the the ui thread and waiting for DownloadStringV3("https://www.google.com").Result be done, which will never be the case, because we do never execute return await resp.Content.ReadAsStringAsync().
Thats why you should do async all the way to the root and never call Result.
The first code works, because you block on the UI thread and don't try to switch to another thread, while waiting for it.
To make the second one working you need to:
private async void button1_Click(object sender, EventArgs e)
{
// make with try catch sure, you catch any error, async void is special. Better is async Task
textBox1.Text = await DownloadStringV3("https://www.google.com");
}
public async Task<string> DownloadStringV3(String url)
{
var resp = await new HttpClient().GetAsync(url);
return await resp.Content.ReadAsStringAsync();
}
My MainPage code-behind:
private void Button_Clicked(object sender, EventArgs e)
{
Query().Wait();
App.Current.MainPage = new Categories();
}
public async Task Query()
{
restaurantsClient = (App.Current as App).restaurantsClient;
try
{
var restaurantsNames = await restaurantsClient.GetCatalogsAsync(1);
}
catch (Exception ex)
{
var x = 0;
}
}
I tried this code too but didn't work, happens the same problem:
async static Task GetRequest(String URL)
{
using (HttpClient client = new HttpClient())
{
// As soon as I try to step over (or even into) this next line, it crashes.
using (HttpResponseMessage response = await client.GetAsync(URL))
{
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync();
Console.WriteLine(data);
}
}
}
}
Rest-API in C#:
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
When the project reach this line it just die without showing any error.
If I do it in the browser it works.
The API is running locally in my PC (Im using Conveyor to expose the API).
Ok I make a video to see better what Im talking about:
https://youtu.be/ONKTipPsEXI
As you can see after I click Next Step in response line stop executing the rest of the code.
That's because you're using .Wait() of a Task on the UI thread (Button.Clicked event is handled on the UI thread) causing a deadlock. The task is waiting for the UI thread to give it control and the UI thread is waiting for the task to complete. The solution to this is to add async modifier to your event handler and use await Query() instead of Query().Wait().
private async void Button_Clicked(object sender, EventArgs e)
{
await Query();
App.Current.MainPage = new Categories();
}
I'd also recommend reading this article by Stephen Cleary about this matter. Moreover, he's made a fabulous series (A Tour of Task) about C# tasks in general.
UPDATE:
After OP's question update and discussion in this answer's comments; he thinks that there's a problem because he can reach the end of GetCatalogsAsync(int) before the end of GetCatalogsAsync(int, CancellationToken). That's completely natural and is to be expected. Solution:
public async System.Threading.Tasks.Task<CatalogsInCategory> GetCatalogsAsync(int id)
{
return await GetCatalogsAsync(id, System.Threading.CancellationToken.None);
}
Here is a question related to my question i found, which did not work.
In my Page load method of web forms I want to call async method
void Page_Load(object sender, EventArgs e)
I want to call async method because I want to be able to call the GetUserInfoAsync method of IdentityModel
This is what I have
protected void Page_Load(object sender, EventArgs e)
{
var token = HttpContext.Current.Request.Headers.Get("Authorization");
GetUserClaims(token).Wait();
}
public async Task GetUserClaims(string token)
{
var client = new HttpClient();
var response = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = "https://localhost:44367/connect/userinfo",
Token = token,
});
var result = response.Claims;
}
Current problem is I never reach result = response.claims part.
GetUserClaims(token).Wait();
was based upon the answer to the question I linked above.
The other option I tried was use the PageAsyncTask and RegisterAsyncTask as mentioned in one of the option in the answer.
PageAsyncTask t = new PageAsyncTask(GetUserClaims(token));
but I get red squiggly thing which complains saying
cannot convert from System.Threading.Task.Task to System.Func<System.Threading.Task.Task>
Actually this helped
protected void Page_Load(object sender, EventArgs e)
{
var token = HttpContext.Current.Request.Headers.Get("Authorization");
PageAsyncTask t = new PageAsyncTask(() => GetUserClaims(token));
// Register the asynchronous task.
Page.RegisterAsyncTask(t);
// Execute the register asynchronous task.
Page.ExecuteRegisteredAsyncTasks();
//GetUserClaims(token).ConfigureAwait(false).GetAwaiter().GetResult();
}
If you absolutely can't make the Page_Load event handler async void, as is suggested by the linked question, then the proper way would be:
GetUserClaims(token).ConfigureAwait(false).GetAwaiter().GetResult();
To avoid the deadlock which happens when you call Wait().
You would also need to add .ConfigureAwait(false) to all nested async calls to ensure that the execution resumes in the same context:
UserInfoResponse response = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = "https://localhost:44367/connect/userinfo",
Token = token,
}).ConfigureAwait(false);
I have a challenge, I need to call many http request and handle each of them.
How to do it, I don't want to wait for get response from one of them and then call next, how to assign a method for process response (like callback).
How can define callback and assign to each of them ?
What you need is an Asynchronous programming model where you create async tasks and later use await keyword for the response.
So essentially you are not waiting for the first async call to finish, you'd just fire as many async tasks as you wish and wait to get a response only when you need the response to move ahead with your program logic.
Have a look at below for more details:
https://msdn.microsoft.com/en-us/library/hh696703.aspx
1) you can call that normaly(noneasync):
public string TestNoneAsync()
{
var webClient = new WebClient();
return webClient.DownloadString("http://www.google.com");
}
2) you can use APM (async):
private void SpecAPI()
{
var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
//req.Method = "HEAD";
req.BeginGetResponse(
asyncResult =>
{
var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
var headersText = formatHeaders(resp.Headers);
Console.WriteLine(headersText);
}, null);
}
private string formatHeaders(WebHeaderCollection headers)
{
var headerString = headers.Keys.Cast<string>()
.Select(header => string.Format("{0}:{1}", header, headers[header]));
return string.Join(Environment.NewLine, headerString.ToArray());
}
3) you can create a callback and asign it,EAP.(async .net 2):
public void EAPAsync()
{
var webClient = new WebClient();
webClient.DownloadStringAsync(new Uri("http://www.google.com"));
webClient.DownloadStringCompleted += webClientDownloadStringCompleted;
}
void webClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
// use e.Result
Console.WriteLine("download completed callback.");
}
4) you can use newer way TAP, cleare way (c# 5). it's recommended:
public async Task<string> DownloadAsync(string url)
{
var webClient = new WebClient();
return await webClient.DownloadStringTaskAsync(url);
}
public void DownloadAsyncHandler()
{
//DownloadAsync("http://www.google.com");
}
threading in this solution is't good approch.(many threads that pending to call http request!)
I've looked at some of the answers for similar questions and can't seem to find something that is applicable to what I'm doing. I need to make a few synchronous requests using HttpWebRequest (some using each verb, GET/PUT/POST/DELETE) and can't seem to get it to work. The example below works great when I manually use a 'refresh' button that I have in the design (works for any verb specified), but when I uncomment the section in 'b_send_Click' it doesn't work. What I'm looking for is a wrapper method that will encapsulate the REST client (the way 'b_send_Click' does in this example) and then take some action when the call is complete (the commented section in 'b_send_Click' as an example). Any ideas? By the way, this works well as a wrapper for async REST calls but I can't get the sync working...
using Microsoft.Phone.Controls;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Windows;
namespace WP8Rest
{
public partial class MainPage : PhoneApplicationPage
{
// global variables
public static string url = "http://mywebsite.com/API/some_api";
public static string request_body = "";
public static HttpWebRequest client = null;
public static HttpWebResponse response = null;
public static string server_response = "";
public static bool request_done = false;
public MainPage()
{
InitializeComponent();
}
private void b_send_Click(object sender, RoutedEventArgs e)
{
rest_request(sender, e);
/*
while (!request_done)
{
Thread.Sleep(100);
}
if (response != null)
{
l_status_code.Text = response.StatusCode.ToString();
l_status_description.Text = response.StatusDescription.ToString();
l_response.Text = server_response;
}
else
{
l_status_code.Text = "0";
l_status_description.Text = "Unable to complete request...";
l_response.Text = "Unable to complete request...";
}
*/
}
private void rest_request(object sender, RoutedEventArgs e)
{
request_done = false;
server_response = "";
request_body = tb_reqbody.Text;
client = (HttpWebRequest)WebRequest.Create(url);
client.Method = tb_verb.Text;
client.AllowAutoRedirect = true;
switch (tb_verb.Text)
{
case "GET":
client.BeginGetResponse(new AsyncCallback(GetResponseCallback), client);
break;
case "PUT":
client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
client.ContentType = "application/json";
break;
case "POST":
client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
client.ContentType = "application/json";
break;
case "DELETE":
client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
client.ContentType = "application/json";
break;
default:
MessageBox.Show("Use GET, PUT, POST, or DELETE.");
return;
}
l_response.Text = "Request sent...";
return;
}
private static void GetRequestStreamCallback(IAsyncResult async_result)
{
HttpWebRequest request = (HttpWebRequest)async_result.AsyncState;
Stream request_body_stream = request.EndGetRequestStream(async_result);
byte[] request_body_bytearray = Encoding.UTF8.GetBytes(request_body);
request_body_stream.Write(request_body_bytearray, 0, request_body.Length);
request_body_stream.Close();
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
private static void GetResponseCallback(IAsyncResult async_result)
{
HttpWebRequest request = (HttpWebRequest)async_result.AsyncState;
response = (HttpWebResponse)client.EndGetResponse(async_result);
Stream response_body_stream = response.GetResponseStream();
StreamReader stream_reader = new StreamReader(response_body_stream);
server_response = stream_reader.ReadToEnd();
response_body_stream .Close();
stream_reader.Close();
response.Close();
request_done = true;
}
private void b_refresh_Click(object sender, RoutedEventArgs e)
{
if (response != null)
{
l_response.Text = server_response;
l_status_code.Text = response.StatusCode.ToString();
l_status_description.Text = response.StatusDescription.ToString();
}
else
{
l_response.Text = "No response...";
}
}
}
}
Per #Industry86 I was able to get Microsoft.Net.Http installed. Changed code to:
private async void b_send_Click(object sender, RoutedEventArgs e)
{
l_response.Text = myMethod();
}
async Task<string> myMethod()
{
string address = "http://dev.getcube.com:65533/rest.svc/API/mirror";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(address);
string responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
Problem now is that it won't compile - "Cannot implicitly convert type 'System.Threading.Tasks.Task' to 'string'. I changed the line in b_send_Click to l_response.Text = (myMethod()).Result; (not sure if this is correct or not) and when I click the 'b_send' button, it turns orange and the server never sees the request.
Creating a new answer to focus on your code changes.
put an await in front of your async method call:
private async void b_send_Click(object sender, RoutedEventArgs e)
{
l_response.Text = await myMethod();
}
when you use .Result, it halts the process until the result returns, thereby halting UI and everything else. Here is an SO answer detailing out the problems with this:
https://stackoverflow.com/a/13703845/311393
Quick lesson: with a void return value, an async method will "fire-and-forget" and never expect a return. With a Task or Task<T> return value, an async method called will halt the calling method until it's completed.
Thus b_send_Click will fire off a thread to do whatever. When it calls myMethod(), which is a Task, with the appropriate await keyword, it will stop in a synchronous fashion and wait till it's completed whatever it's doing.
And myMethod() has multiple async method calls but those have await's on them as well, so the thread will wait for those to complete synchronously as well.
Then it returns back to your text field and the UI, I assume in WP8, listens to changes to it's text field asynchronously.
Personally, I would use the HTTPClient that exists in Windows 8 (it's pretty awesome) and currently possibly still in beta for WP8 (https://nuget.org/packages/Microsoft.Net.Http) if not fully released already. The syntax is much smaller and simpler.
Or use RestSharp (http://restsharp.org/) but the async/await stuff isn't as robust. RestSharp is really good at serialization though.
that said, you also need to learn how Async operations occur. You are calling an Asynchronous operation and moving on without an "await". Therefore:
l_response.Text = server_response;
will not get set because, without the Thread.Sleep(100), the code will have fired off the async call and move on and server_response will still be null by the time it gets to that part.
if you want to want to wait for the return of that call, you need to use an "await" command and include the async signifier in the method declaration and return a Task (where object can be whatever you're intending to return). Example using HttpClient:
async Task<string> myMethod()
{
string address = "http://mywebsite.com/API/some_api";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(address);
string responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
and the resulting string "responseText" will have the JSON content to parse. Of course, if you're looking for a stream, its there too.
And you also have to remember that this method itself will require an await from the UI thread and the declaration will be required to be async:
private async void b_send_Click(object sender, RoutedEventArgs e)
{
someTextBox.Text = myMethod();
}
and event handlers should typically be the only async methods that return void.