Reputation: 9144
I am trying get a JSON response from a web API.
I am able to retrive response in Console application with similar code, however in UWP await httpClient.GetAsync(uri);
does not work as expected.
public static async Task<double> GetJson()
{
using (var httpClient = new HttpClient())
{
Uri uri= new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
HttpResponseMessage response = await httpClient.GetAsync(uri);
//Below code is not relevent since code is failing on await
var result = response.Content.ReadAsStringAsync();
var jsonResponse = Json.ToObjectAsync<ExchangeRate>(result);//
jsonResponse.Wait();//
ExchangeRate exchangeRateObj = jsonResponse.Result;//
return 1.2;//
}
}
Code behind:
private void Button_Click(object sender,RoutedEventArgs e){
var ROC__ = MyClass.GetJson();
ROC__.Wait();
currency_.ROC = ROC__.Result;
}
What is does not work here means?
It is supposed to connect to URL and fetch the response and response should be assigned some value. Instead on debug with either step into or Continue the control exits the current line skips subsequent lines too. (I have put debug on next lines too),The app just freezes.
I refereed similar codes for JSON parsing with HTTPClient
on Stackoverflow and other blogs , its suggested to use System.Net.Http
or Windows.Web.Http
Related Question : how-to-get-a-json-string-from-url
I think tasks are run and its going on forever wait mode , which seems strange as debug mode doesnt show code being run , it just show ready
. There is no exception as well.
Am I doing something wrong or missing some Nuget reference?
Please suggest.
PS : Its the same case with httpClient.GetStringAsync
method too.
On Console app this line works but not on UWP
var json = new WebClient().DownloadString("https://api.cryptonator.com/api/ticker/btc-usd");
Not duplicate of httpclient-getasync-never-returns-on-xamarin-android
Upvotes: 1
Views: 1688
Reputation: 1953
There're several errors with the code specified that needs to be cured. First, mark your event handler async
:
private async void Button_Click(object sender, RoutedEventArgs e)
Second, await GetJson
and since this is an asynchronous method, it is a better convention to add the suffix "Async" to it's name; therefore, await GetJsonAsync
:
currency_.ROC = await MyClass.GetJsonAsync();
Now, back to GetJsonAsync
itself, ReadAsStringAsync
and ToObjectAsync
should be awaited too:
private static async Task<double> GetJsonAsync()
{
using (var httpClient = new HttpClient())
{
Uri uri = new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
HttpResponseMessage response = await httpClient.GetAsync(uri);
string result = await response.Content.ReadAsStringAsync();
//Below code is not relevent since code is failing on await
ExchangeRate exchangeRateObj = await Json.ToObjectAsync<ExchangeRate>(result);
return 1.2;//
}
}
The reason it wasn't working before is that there was a deadlock caused by the context switch between the await
call and the synchronous block of .Wait()
. Find more about that here.
Upvotes: 2
Reputation: 1638
So I tried this by myself. Unfortunately your informations where not complete so a little header:
For the Json-Handling I used Newtonsoft, because I didnt found the Json.ToObjectAsync
in my UWP environment.
To create the ExchangeRate
- class I used Json2CSharp.
Here are the ExchangeRate classes:
public class ExchangeRate
{
public string Error { get; set; }
public bool Success { get; set; }
public Ticker Ticker { get; set; }
public int Timestamp { get; set; }
}
public class Ticker
{
public string @Base { get; set; }
public string Change { get; set; }
public string Price { get; set; }
public string Target { get; set; }
public string Volume { get; set; }
}
I changed the Button_Click
-Method to an async void
. Normally its not recommend to have an async void
instead of a async Task
. But because it is a Handler from a UI-Element its not a problem, because the source will not wait anyway and you shouldn't call this methode directly from your code-behind.
The Button_Click
-Method:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var ROC__ = await MyClass.GetJson();
//Do whatever you want with the result.
//Its a double, maybe you want to return an ExchangeRate objcet insted
}
Inside of your GetJson
-Method, you need to add an await for your async operations, or add the .Wait()
directly after the method and not in a new line. You need to do this, because the Task automatically starts to run, when you call the async operation and the .Wait()
comes to late. So your GetJson-Method looks like this:
public static async Task<Double> GetJson()
{
using (var httpClient = new HttpClient())
{
Uri uri = new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
HttpResponseMessage response = await httpClient.GetAsync(uri);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var result = await response.Content.ReadAsStringAsync();
ExchangeRate rate = JsonConvert.DeserializeObject<ExchangeRate>(result); //Newtonsoft
return 1.2;
}
else
{
return -1; //Example value
}
}
}
In addition I added a check, if the Request was successful, to be sure, that we have a response. How I said before: I think you should return a ExchangeRat
e-object instead of a double, but this depends on your context.
Upvotes: 1
Reputation: 9867
Your code works fine. I regenerated it here below. Just get rid of that wonky wait call you're doing.
Do this. Create a new uwp app, paste in the below code and put a breakpoint on the return and see that it gets executed.
It will work regardless if you make your button handler async or not. If you don't make it asnyc though then the request won't be executed asynchronously
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SomeClass.GetJson();
}
}
public class SomeClass
{
public static async Task<double> GetJson()
{
using (var httpClient = new HttpClient())
{
Uri uri = new Uri("https://api.cryptonator.com/api/ticker/btc-usd");
HttpResponseMessage response = await httpClient.GetAsync(uri);
return 1.2;
}
}
}
Might I take this moment for a shameless plug of my UWP lib. It does this work for you.
https://github.com/DotNetRussell/UWPLibrary
In the BasecodeRequestManager.cs file there's a BeginRequest function that does this work async for you. The lib has many many other features as well.
Upvotes: 1