UWP await on HttpClient not working - c#

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
Its not Xamarin , even though it is C# based , my code is different , its not WebApiClient or GetInformationAsync method which I am concerned about.

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.

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.

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 ExchangeRate-object instead of a double, but this depends on your context.

Related

Program ends before async result is received

I am writing a program to interact with the Spotify API via a command line.
I have some code here to take a command, and then execute the relevant function to retrieve data from Spotify.
This code shows the problem, I have left out irrelevant code.
public class CommandHandler
{
public async void HandleCommands()
{
var spotifyCommand = GetCommand();
if (spotifyCommand == SpotifyCommand.Current)
{
WriteCurrentSong(await new PlayerController().GetCurrentlyPlayingAsync());
}
if (spotifyCommand == SpotifyCommand.NextTrack)
{
WriteCurrentSong(await new PlayerController().NextTrackAsync());
}
Console.ReadLine();
//end of program
}
}
public class PlayerController
{
public async Task<SpotifyCurrentlyPlaying> NextTrackAsync()
{
using (var httpClient = new HttpClient())
{
//removed code to set headers etc
//Skip Track
var response = await httpClient.PostAsync("https://api.spotify.com/v1/me/player/next", null);
if (response.StatusCode != HttpStatusCode.NoContent)
{
//code to handle this case, not important
}
return await GetCurrentlyPlayingAsync();
}
}
public async Task<SpotifyCurrentlyPlaying> GetCurrentlyPlayingAsync()
{
using (var httpClient = new HttpClient())
{
//removed code to set headers etc
var response = await httpClient.GetAsync("https://api.spotify.com/v1/me/player/currently-playing");
response.EnsureSuccessStatusCode();
return JsonSerializer.Deserialize<SpotifyCurrentlyPlaying>(await response.Content.ReadAsStringAsync());
}
}
}
The two if statements in HandleCommands() call into PlayerController and await the result of the method. For some reason if I use await PlayerController.MethodCall() the call is made, however, the result does not return before the program finishes executing.
Strangely, this is not an issue if I use PlayerController.MethodCall().Result.
Any help will be greatly appreciated, as I would really rather not use .Result. Thanks!
Signature of the HandleCommands is an issue
public async void HandleCommands()
{
// ...
}
You are not showing how this method is called, but I assume it is something like below:
var handler = new CommandHandler();
handler.HandleCommands();
Because of async void method doesn't return Task and caller can not "observe" it's completion.
So application finishes without waiting for task to complete.
To fix - change method signature to below and await for task to complete
public async Task HandleCommands()
{
// ...
}
var handler = new CommandHandler();
await handler.HandleCommands();

Async Method not Returning a String

I am trying to get some weather data from Open Weather Maps and l use HTTPClient and API ASP.NET in C#.
My code keeps returning:
System.Threading.Tasks.Task`1[System.String]
I've made my methods async and await but I still get the above returned. I thought making it await would return the value.
I am just trying to get the string from Open Weather Maps, I'll worry about parsing it to JSON once I have this working. Here is my code, "MY_APPID" is replaced with my API key, I just removed it here.
My main:
private async Task<string> GetLocationJson()
{
const string APPID = "(MY_APPID)";
const string LOCATIONID = "2172797";
string jsonAsString = "";
string callStringJson = "api.openweathermap.org/data/2.5/weather?id=" + LOCATIONID + "&appid=" + APPID;
ApiCalls weatherApi = new ApiCalls(callStringJson);
jsonAsString = await weatherApi.GetLocationJson();
return jsonAsString;
}
//ShowLocationJson is called on button click
protected void ShowLocationJson(object sender, EventArgs e)
{
litOutput.Text = GetLocationJson().ToString();
}
And my ApiCalls Class is:
public class ApiCalls
{
HttpClient client = new HttpClient();
Uri url;
public ApiCalls(string link)
{
url = new Uri("https://" + link);
}
public async Task<string> GetLocationJson()
{
string content = await client.GetStringAsync(url);
return content;
}
}
url variable is being passed the correct values so I know it's ok up to there.
Im using ASP.NET Framework 4.5 as well
As Steve and mason have already noticed you have to await all methods returning Task. You need to change your ShowLocationMethod to:
protected async void ShowLocationJson(object sender, EventArgs e)
{
litOutput.Text = await GetLocationJson();
}
You receive System.Threading.Tasks.Task1[System.String] because you call method ToString() of Task object. The default implementation of the ToString method returns the fully qualified name of the type of the object: Task<String>

C# Button to execute ASync Method. (Attempt to Put to Web Api (Phillips Hue))

So i'm just trying make an app, push a button and turn off a light. Simple, right? But i'm a dolt.
So i've never played with Web APIs before (or any api, really). I'm following This MS Doc to get a grip on it. Modifying it to make it work but it doesn't (obviously).
In Button_Click the RunUpdate errors 'MainPage.Runupdate()' is a method, which is not valiud in the given context'
This is probably an easy fix but i'm sure to run into more. Anyone up to help me out?
Thanks!
Here's the code:
public sealed partial class MainPage : Page
{
static HttpClient client = new HttpClient();
public class Light
{
//public string ID { get; set }
public string Name { get; set; }
public string state { get; set; }
}
public MainPage()
{
this.InitializeComponent();
client.BaseAddress = new Uri("http://192.168.0.100/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
static async Task<Light> GetLightAsync(string path)
{
Light light = null;
HttpResponseMessage response = await client.GetAsync(path);
if(response.IsSuccessStatusCode)
{
light = await response.Content.ReadAsAsync<Light>();
}
return light;
}
static async Task<Light> UpdateLightAsync(Light light)
{
HttpResponseMessage response = await client.PutAsJsonAsync(
$"api/api/RjplsYoXQvdTl11DOVIo92SKNB7vYRfwZvqCzvDK/lights/1/state/", light.state);
response.EnsureSuccessStatusCode();
light = await response.Content.ReadAsAsync<Light>();
return light;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
RunUpdate.GetAwaiter().GetResult();
}
static async Task RunUpdate()
{
Light light = new Light { Name = "Unknown", state = "off" };
light = await UpdateLightAsync(light);
}
}
Well, RunUpdate() is a method - you forgot parentheses in your RunUpdate call from the Button_Click :)

DEADLOCK Xamarin

I am trying to develop my first Xamarin App.
I have two classes for webservices:
RestClient: which makes the request and get the json String
Helper Class: which should get the Json String and deserialized it to the Object Type.
I know that the Wait Method is not the best option, but I have tried so many different suggested versions but it doesn't work. Every try ended in a Deadlock. Every Thread works in the Background. How can I get my Data back to the UI?
code of my RestClient Class:
class RestClient
{
public static string base_url = #"our Restservice address";
// public string completeUrl { get; set; }
HttpClient client;
public RestClient()
{
client = new HttpClient();
client.BaseAddress = new Uri(base_url);
//client.MaxResponseContentBufferSize = 256000;
}
public async Task<String> GetData(string endpoint)
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(endpoint);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return result;
}
else
{
return null;
}
}
Code of my Helper Class
public SupplierHelper()
{
}
public async Task<Suppliers> getData()
{
RestClient restClient = new RestClient();
string result = await restClient.GetData("suppliers/13");
return JsonConvert.DeserializeObject<Suppliers>(result);
}
Code of my VievModelClass
public class AccountViewModel : BaseViewModel
{
public static SupplierHelper supHelper;
public static Suppliers sup;
public string Name { set; get; }
public string Address { set; get; }
public AccountViewModel()
{
loadSupplier().Wait();
}
public async Task loadSupplier()
{
supHelper = new SupplierHelper();
sup = await supHelper.getData();
}
}
Task.Run(loadSupplier).Wait(); will solve your problem.
Your deadlock is caused by async method trying to execute continuation on caller thread, but caller thread is blocked until that async method completes.
.Wait() is worse than "not the best option" - it'll actively cause a circular wait in any environment that has a synchronization context. Read this article for more details.
If you must call this to init the object correctly, you could use an async Factory method or something.

async method callback with Task.ContinueWIth?

I have a method that pulls some HTML via the HttpClient like so:
public static HttpClient web = new HttpClient();
public static async Task<string> GetHTMLDataAsync(string url)
{
string responseBodyAsText = "";
try
{
HttpResponseMessage response = await web.GetAsync(url);
response.EnsureSuccessStatusCode();
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
catch (Exception e)
{
// Error handling
}
return responseBodyAsText;
}
I have another method that looks like so:
private void HtmlReadComplete(string data)
{
// do something with the data
}
I would like to be able to call GetHTMLDataAsync and then have it call HtmlReadComplete on the UI thread when the html has been read. I naively thought this could somehow be done with something that looks like
GetHTMLDataAsync(url).ContinueWith(HtmlReadComplete);
But, I can't get the syntax correct, nor am I even sure that's the appropriate way to handle it.
Thanks in advance!
public async void ProcessHTMLData(string url)
{
string HTMLData = await GetHTMLDataAsync(url);
HTMLReadComplete(HTMLData);
}
or even
public async void ProcessHTMLData(string url)
{
HTMLReadComplete(await GetHTMLDataAsync(url));
}
You're close, but ContinueWith() takes a delegate with Task as its parameter, so you can do:
GetHTMLDataAsync(url).ContinueWith(t => HtmlReadComplete(t.Result));
Normally, you should be careful with using Result together with async, because Result blocks if the Task hasn't finished yet. But in this case, you know for sure that the Task is complete, you Result won't block.

Categories