I am trying to fetch data from an API.
When I write all code in one method like as follows it work fine.
private async void btvalidate_Click(object sender, RoutedEventArgs e)
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("mybaseaddress");
HttpResponseMessage response = await client.GetAsync("mylocaluri");
if (response.IsSuccessStatusCode)// check whether response status is true
{
var data = response.Content.ReadAsStringAsync();//read the data in the response
var msg = JsonConvert.DeserializeObject<myclassname>(data.Result.ToString());//convert the string response in json format
validate.DataContext = msg;// assign the data received to stackpanel
}
}
catch (Exception ex)
{
MessageBox.Show("Somethimng went wrong" + ex);
}
}
But when I try to write this code in a method of a separate class and call it from a click event as follows, it hangs on click of event and has status of data as WaitingforActivation...
public class API
{
public async Task<string> getAPI(string uri)
{
string data1 = null;
var data=data1;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("mybaseaddress");
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)// check whether response status is true
{
data = response.Content.ReadAsStringAsync().Result;//read the data in the response
}
}
return data;
}
}
private void btcount_Click(object sender, RoutedEventArgs e)
{
var data = api.getAPI("mylocaluri");
var msg = JsonConvert.DeserializeObject<myclassname>(data.Result.ToString());//convert the string response in json format
validate.DataContext = msg;// assign the data received to stackpanel
}
Can someone tell me what I'm doing wrong?
Thanks for help in advance.
You're causing a deadlock by calling Result, which I explain in full on my blog.
The best solution is to use async "all the way", as I describe in my MSDN article on async best practices.
In this particular case, replace Result with await:
private async void btcount_Click(object sender, RoutedEventArgs e)
{
var data = await api.getAPI("mylocaluri");
var msg = JsonConvert.DeserializeObject<myclassname>(data.ToString());//convert the string response in json format
validate.DataContext = msg;// assign the data received to stackpanel
}
On a side note, consider renaming getAPI to GetApiAsync, to follow common naming patterns.
Related
I'm trying to convert a curl request example from an API for Jira into a C# request.
This is the original Curl Request example that JIRA provides:
curl \
-D- \
-u charlie:charlie \
-X GET \
-H "Content-Type: application/json" \
http://localhost:8080/rest/api/2/search?jql=assignee=charlie
Which I've translated into the below code for JIRA:
However the response lines don't work - because I've cobbled together a few examples and got a bit stuck!
var myTask = curlRequestAsync(); // call your method which will return control once it hits await
string result = myTask.Result();
// .Result isn't defined - but I'm not sure how to access the response from my request!
Any help would be appreciated as I'm really stuck!
Full example:
public partial class WebForm2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var myTask = curlRequestAsync(); // call your method which will return control once it hits await
string result = myTask.Result();
// wait for the task to complete to continue
}
protected async System.Threading.Tasks.Task curlRequestAsync()
{
try
{
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("GET"), "http://myurl.co.uk/rest/api/2/search?jql=assignee=bob"))
{
var base64authorization = Convert.ToBase64String(Encoding.ASCII.GetBytes("username:password"));
request.Headers.TryAddWithoutValidation("Authorization", $"Basic {base64authorization}");
var result = await httpClient.SendAsync(request);
return result;
}
}
}
catch (Exception ex)
{
error.InnerText = ex.Message;
}
}
}
you should return Task<System.Net.Http.HttpResponseMessage>
protected async System.Threading.Tasks.Task<HttpResponseMessage> curlRequestAsync()
{
try
{
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("GET"), "http://myurl.co.uk/rest/api/2/search?jql=assignee=bob"))
{
var base64authorization = Convert.ToBase64String(Encoding.ASCII.GetBytes("username:password"));
request.Headers.TryAddWithoutValidation("Authorization", $"Basic {base64authorization}");
var result = await httpClient.SendAsync(request);
return result;
}
}
}
catch (Exception ex)
{
error.InnerText = ex.Message;
}
}
after that you could access response of your task:
var myTask = curlRequestAsync();
var result = myTask.Result.Content.ReadAsStringAsync().Result;
Due to the async keyword, your methods signature, as "seen by the compiler", within the methods's scope is converted:
protected async Task Foo()
will become
protected void Foo();
To return a value, with the async keyword, you should use this signature:
protected async Task<T> Foo()
which results in
protected T Foo()
As for the caller , the signature stays the same.
On Task, Result is not defined because by its nature it doesn't have a return value from the task. A Task<T> on the other hand has a Result.
So, in order to get an "result", (Result is not defined on Task (Wait is), you should use Task<T>, on which Result is defined.
In your case you should change the signature to:
protected async System.Threading.Tasks.Task<WhatEverTypeYouAreReturning> curlRequestAsync()
You will now able to get the Result or await the call if you are in an async scope. The latter is preferred since it will keep your method async which has some benefits regarding to the use of resources.
When testing my web API with Postman my API get executes fine!
When it comes to running the code with HttpClient in my client application the code executes without error but without the expected result on the server.
What could be happening?
From my client application:
private string GetResponseFromURI(Uri u)
{
var response = "";
HttpResponseMessage result;
using (var client = new HttpClient())
{
Task task = Task.Run(async () =>
{
result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
});
task.Wait();
}
return response;
}
Here is the API controller:
[Route("api/[controller]")]
public class CartsController : Controller
{
private readonly ICartRepository _cartRepo;
public CartsController(ICartRepository cartRepo)
{
_cartRepo = cartRepo;
}
[HttpGet]
public string GetTodays()
{
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Add")]
public string GetIncrement()
{
var cart = new CountedCarts();
_cartRepo.Add(cart);
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Remove")]
public string GetDecrement()
{
_cartRepo.RemoveLast();
return _cartRepo.GetTodaysCarts();
}
}
Note these API calls work as expected when called from Postman.
You shouldn't use await with client.GetAsync, It's managed by .Net platform, because you can only send one request at the time.
just use it like this
var response = client.GetAsync("URL").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var dataObjects = response.Content.ReadAsAsync<object>().Result;
}
else
{
var result = $"{(int)response.StatusCode} ({response.ReasonPhrase})";
// logger.WriteEntry(result, EventLogEntryType.Error, 40);
}
You are doing fire-and-forget approach. In your case, you need to wait for the result.
For example,
static async Task<string> GetResponseFromURI(Uri u)
{
var response = "";
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
}
return response;
}
static void Main(string[] args)
{
var t = Task.Run(() => GetResponseFromURI(new Uri("http://www.google.com")));
t.Wait();
Console.WriteLine(t.Result);
Console.ReadLine();
}
Simple sample used to get page data.
public string GetPage(string url)
{
HttpResponseMessage response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
string page = response.Content.ReadAsStringAsync().Result;
return "Successfully load page";
}
else
{
return "Invalid Page url requested";
}
}
I've had a problem with chace control when using httpclient.
HttpBaseProtocalFilter^ filter = ref new HttpBaseProtocolFilter();
filter->CacheControl->ReadBehavior = Windows::Web::Http::Filters::HttpCacheReadBehavior::MostRecent;
HttpClient^ httpClient = ref new HttpClient(filter);
I'm not really sure what the expected results are or what results your getting at all so this is really just a guessing game right now.
When I POST something using HttpClient I found adding headers by hand seemed to work more often than using default headers.
auto httpClient = ref new HttpClient();
Windows::Web::Http::Headers::HttpMediaTypeHeaderValue^ type = ref new Windows::Web::http::Headers::HttpMediaTypeHeaderValue("application/json");
content->Headers->ContentType = type;
If I don't do these 2 things I found, for me anyways, that half the time my web requests were either not actually being sent or the headers were all messed up and the other half of the time it worked perfectly.
I just read a comment where you said it would only fire once, that makes me think it is the cachecontrol. I think what happens is something (Windows?) sees 2 requests being sent that are the exact same, so to speed things up it just assumes the same answer and never actually sends the request a 2nd time
Currently working with the outlook api, even tough I usually work with the outlook library acquired via Nuget; I have reached a limitation where I am not able to accept event invitations. So I proceeded in making a a restful call out to the the outlook api. However, when I am making the call I am getting the following message {"error":{"code":"InvalidMethod","message":"An action can only be invoked as a 'POST' request."}} when executing the call.
Bad Code
class Program
{
static void Main(string[] args)
{
var testAccept = ExecuteClientCall.AcceptEvent().Result;
}
public static async Task<bool> AcceptEvent()
{
AuthenticationContext authenticationContext = new AuthenticationContext(CrmPrototype.Helpers.AuthHelper.devTenant);
try
{
var token = await GetTokenHelperAsync(authenticationContext, CrmPrototype.Helpers.AuthHelper.OutlookAuthenticationEndpoint);
string requestUrl = "https://outlook.office.com/api/v2.0/Users/***#nowwhere.com/events('AAQkAGZiNDQxZTVkLWQzZjEtNDdjNy04OTc4LTM4NmNjM2JiOTRjNAAQAFpV0CnWR0FIpWFYRtszPHU=')/accept";
HttpClient hc = new HttpClient();
hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var method = new HttpMethod("POST");
var request = new HttpRequestMessage(method, requestUrl)
{
Content = new StringContent("{SendResponse: true}", Encoding.UTF8, "application/json")
};
HttpResponseMessage hrm = await hc.GetAsync(requestUrl);
if (hrm.IsSuccessStatusCode)
{
string jsonresult = await hrm.Content.ReadAsStringAsync();
var stophere = 0;
}
else
{
return false;
}
return true;
}
catch (Exception ex)
{
throw;
}
}
}
Maybe the reason is that you called
hc.GetAsync(requestUrl);
The doc said that this method:
Sends a GET request to the specified Uri as an asynchronous operation.
Try:
PostAsync(Uri, HttpContent)
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.118).aspx
Hope this help you.
Your variable request contains an HttpRequestMessage object that you have created, but your code presently doesn't do anything with it.
Try replacing the line
HttpResponseMessage hrm = await hc.GetAsync(requestUrl);
(which, as pointed out by the other answer, makes a GET request), with
HttpResponseMessage hrm = await hc.SendAsync(request);
I am currently trying to extract data from my Web Service. I managed to get success response from the Web Service using HTTPClient. However, I am unable to extract specific values. For instance, my JSON document read as
{"d":[{"__type":"Info:#website.Model","infoClosingHours":"06:00:00 PM","infoID":1,"infoOpeningDays":"Monday","infoOpeningHours":"09:00:00 AM","infoStatus":"Open"}]}
I want to get the infoOpeningDays, however, I am unable to do it.
I tried using
dynamicObject.GetType().GetProperty("infoOpeningDays").GetValue(dynamicObject, null);
dynamicObject["infoOpeningDays"];
But it kept giving me null.
Here's my code
private async void GetData(object sender, EventArgs e)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("ip");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try{
HttpResponseMessage response = client.GetAsync("WebServices/information.svc/GetInformationJSON").Result;
if (response.IsSuccessStatusCode)
{
string jsonString = await response.Content.ReadAsStringAsync();
dynamic dynamicObject = JsonConvert.DeserializeObject(jsonString);
//string abc = dynamicObject.IEnumerator.[0].IEnumerator.[0].IEnumerator.[0].IEnumerator.[5].Name;
string abc = dynamicObject.GetType().GetProperty("infoOpeningDays").GetValue(dynamicObject, null);
}
}
catch
{
}
}
You should access the properties directly from your dynamic object like this:
dynamic dynamicObject = JsonConvert.DeserializeObject(json);
string infoClosingHours = dynamicObject.d[0].infoClosingHours;
Or this is the same
string infoClosingHours = dynamicObject.d[0]["infoClosingHours"];
I am new To C# programming.
I want to develop a simple Weather App using openweathermap API's.
I want to download and read contents of a file from an URL.
This is my Code to download file contents:
class WebClientToDownload
{
string webresponse;
public async void DownloadFile(string url)
{
string baseurl = "http://api.openweathermap.org/data/2.5/forecast/daily?q=";
StringBuilder sb = new StringBuilder(baseurl);
sb.Append(url + "&mode=json&units=metric&cnt=7");
string actual = sb.ToString();
HttpClient http = new System.Net.Http.HttpClient();
HttpResponseMessage response = await http.GetAsync(actual);
webresponse = await response.Content.ReadAsStringAsync();
}
public string StringReturn()
{
return webresponse;
}
string passed to the function is the name of city.
This is MainPage Code where I call those functions:
string JSONData;
private void GetWeatherButton_Click(object sender, RoutedEventArgs e)
{
WebClientToDownload Cls = new WebClientToDownload();
Cls.DownloadFile(GetWeatherText.Text);
JSONData = Cls.StringReturn();
JSONOutput.Text = JSONData;
}
I am getting an error at last line of code says as
An exception of type 'System.ArgumentNullException' occurred in mscorlib.dll but was not handled in user code
Additional information: Value cannot be null.
It looks like it was down to your use of await. Basically, await will pass control back to the calling function and allow it to continue until it is awaited, which isn't happening in your case so it is calling Cls.StringReturn() before the data has been returned. You can change as follows:
In your form:
string JSONData;
// Note the async keyword in the method declaration.
private async void GetWeatherButton_Click(object sender, EventArgs e)
{
WebClientToDownload Cls = new WebClientToDownload();
// Notice the await keyword here which pauses the execution of this method until the data is returned.
JSONData = await Cls.DownloadFile(GetWeatherText.Text);
JSONOutput.Text = JSONData;
}
In your download class:
class WebClientToDownload
{
// Notice this now returns a Task<string>. This will allow you to await on the data being returned.
public async Task<string> DownloadFile(string url)
{
string baseurl = "http://api.openweathermap.org/data/2.5/forecast/daily?q=";
StringBuilder sb = new StringBuilder(baseurl);
sb.Append(url + "&mode=json&units=metric&cnt=7");
string actual = sb.ToString();
HttpClient http = new System.Net.Http.HttpClient();
HttpResponseMessage response = await http.GetAsync(actual);
// Return the result rather than setting a variable.
return await response.Content.ReadAsStringAsync();
}
}
I've tested and it returned valid data but if any of this isn't clear please let me know.