I have the following method in Xamarin that makes an API call that returns a list of Items. When I start Visual Studio and run it returns a string of JSON with the information I expect it to. However when I run it several times and more often than not it hits the string returnedJson = await client.GetStringAsync line then just breaks out of the method, returning nothing.
I dont think its related to restarting Visual Studio, because sometimes after it not working several times it will work once again then stop again.
public async Task<IEnumerable<ItemModel>> GetItemsAsync(bool forceRefresh = false)
{
var baseAddr = new Uri("https://www.domain.co.uk");
var client = new HttpClient
{
BaseAddress = baseAddr
};
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Settings.ApiToken);
// NOW PROTECTED
try
{
string returnedJson = await client.GetStringAsync("/api/Items/Index");
var convert = JsonConvert.DeserializeObject<List<ItemModel>>(returnedJson);
return convert;
}
catch (Exception ex)
{
return null;
}
return null;
}
The WebAPI method -
[Authorize]
[HttpGet("[action]")]
public async Task<List<ItemModel>> Index()
{
return await itemService.Get();
}
I am using the Microsoft Cognitive Face API for implementing the Face Detection to Authenticate the person and login to the application.
For that, I am creating a PersonGroup and add the Person and then Face of Person. Train Person group and so on.
I want to implement it in the MVC Web Application. I have written the same code in MVC Web Application. But the API is not returning anything and just hanged at the Http request call.
So Do we need to get some different API Key for a Web application or Do we need to make any changes in Web.Config file to make a successful call?
I have tried the same code in the Console Application and WPF Application. There everything works perfectly fine.
public class HttpClientHelper
{
HttpClient httpClient = new HttpClient();
private const string subscriptionKey = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
private const string faceEndpoint = "https://southeastasia.api.cognitive.microsoft.com/face/v1.0/";
public HttpClientHelper()
{
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
httpClient.BaseAddress = new Uri(faceEndpoint);
}
public async Task<T> GetAsync<T>(string url)
{
var response = await httpClient.GetAsync(url);
string contentString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(contentString);
}
}
public static async Task CreatePersonGroup(UserGroup userGroup)
{
bool isGroupExists = false;
PersonGroup personGroup;
try
{
HttpClientHelper httpClientHelper = new HttpClientHelper();
var response = httpClientHelper.GetAsync<PersonGroup>(string.Format("persongroups/{0}", userGroup.UserGroupId)).Result;
isGroupExists = true;
}
catch (APIErrorException ex)
{
if (ex.Body.Error.Code == "PersonGroupNotFound")
isGroupExists = false;
}
if (isGroupExists == false)
{
await faceClient.PersonGroup.CreateAsync(userGroup.UserGroupId, userGroup.Name);
}
}
I expect the same code must be working fine in Web Application also. As there is not a big logic in it. Just a simple API call.
private const string faceEndpoint =
"https://southeastasia.api.cognitive.microsoft.com/";
hopefully, it should work
I know this should be simple, so I am obviously missing something.
I am writing a very simple Xamarin.Forms application with this as the MainPage.xaml.cs of the Shared project.
public partial class MainPage : ContentPage
{
public MyClass myClassInstance { get; set; }
public MainPage()
{
InitializeComponent();
DownloadDataAsync();
}
private async void DownloadDataAsync()
{
string page = #"http://www.blaw.com:80/foo/bar";
try
{
HttpClient client = new HttpClient();
var uri = new Uri(string.Format(page, string.Empty));
var response = await client.GetAsync(uri); <--- BONK!
if (response.IsSuccessStatusCode)
{
var responseString = await response.Content.ReadAsStringAsync();
XmlSerializer ser = new XmlSerializer(typeof(MyClass));
using (TextReader tr = new StringReader(responseString))
{
myClassInstance = (MyClass)ser.Deserialize(tr);
}
}
else
System.Diagnostics.Debug.WriteLine(response.StatusCode.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
When I run this on my Android device, it never gets past the GetAsync line (marked with <-- BONK!). It doesn't throw an exception, and in never makes it to the if statement, it just returns from the method without doing anything at all.
I have enabled the ACCESS_NETWORK_STATE and INTERNET permissions in the Android Manifest.
What am I missing?
Calling asynchronous method from constructor is not a good idea. This will work in console applications but not on UI applications. Because this will block the UI thread, and control never returns to the code due to deadlock.
If you can call your async method in OnAppearing, it should work fine.
I am really stuck on this as I have searched far and wide for a solution for an asyncpost using Web API but couldnt find anything. Essentially, its got to make a POST call using HttpClient to the relevant controller class AddMenuItem using Web API but it just doesn't work. It simply throws an error of a 404 Error and cannot see the controller method. Any reasons why and solution for this would be very helpful!
// Async Post Call
public static async void asyncPost()
{
using (var client = new HttpClient())
{
try
{
var values = new System.Collections.Generic.Dictionary<string, string>();
values.Add("ItemName", "Pepperoni Pizza");
var content = new FormUrlEncodedContent(values);
string baseAddress = "http://localhost:9000/";
HttpResponseMessage response3 = await client.PostAsync(baseAddress + "api/values/AddMenuItem", content);
if (response3.StatusCode == System.Net.HttpStatusCode.OK)
{
// Do something...
}
}
catch (OperationCanceledException) { }
}
}
// POST api/values
public void AddMenuItem([FromBody]string itemName)
{
//Should go in here when PostAync is called
}
Don't use async void; use async Task instead.
public static async Task PostAsync()
Then your controller can call it with await:
public async Task AddMenuItem([FromBody]string itemName)
{
await PostAsync(..);
}
I have been experimenting with WP7 apps today and have hit a bit of a wall.
I like to have seperation between the UI and the main app code but Ive hit a wall.
I have succesfully implemented a webclient request and gotten a result, but because the call is async I dont know how to pass this backup to the UI level. I cannot seem to hack in a wait for response to complete or anything.
I must be doing something wrong.
(this is the xbox360Voice library that I have for download on my website: http://www.jamesstuddart.co.uk/Projects/ASP.Net/Xbox_Feeds/ which I am porting to WP7 as a test)
here is the backend code snippet:
internal const string BaseUrlFormat = "http://www.360voice.com/api/gamertag-profile.asp?tag={0}";
internal static string ResponseXml { get; set; }
internal static WebClient Client = new WebClient();
public static XboxGamer? GetGamer(string gamerTag)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null);
return SerializeResponse(response);
}
internal static XboxGamer? SerializeResponse(string response)
{
if (string.IsNullOrEmpty(response))
{
return null;
}
var tempGamer = new XboxGamer();
var gamer = (XboxGamer)SerializationMethods.Deserialize(tempGamer, response);
return gamer;
}
internal static string GetResponse(string url, string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
Client.Credentials = new NetworkCredential(userName, password);
}
try
{
Client.DownloadStringCompleted += ClientDownloadStringCompleted;
Client.DownloadStringAsync(new Uri(url));
return ResponseXml;
}
catch (Exception ex)
{
return null;
}
}
internal static void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
ResponseXml = e.Result;
}
}
and this is the front end code:
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
var xboxGamer = xboxManager.GetGamer();
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
Now I understand I need to place something in ClientDownloadStringCompleted but I am unsure what.
The problem you have is that as soon as an asynchronous operation is introduced in to the code path the entire code path needs to become asynchronous.
Because GetResponse calls DownloadStringAsync it must become asynchronous, it can't return a string, it can only do that on a callback
Because GetGamer calls GetResponse which is now asynchronous it can't return a XboxGamer, it can only do that on a callback
Because GetGamerDetails calls GetGamer which is now asynchronous it can't continue with its code following the call, it can only do that after it has received a call back from GetGamer.
Because GetGamerDetails is now asynchronous anything call it must also acknowledge this behaviour.
.... this continues all the way up to the top of the chain where a user event will have occured.
Here is some air code that knocks some asynchronicity in to the code.
public static void GetGamer(string gamerTag, Action<XboxGamer?> completed)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null, (response) =>
{
completed(SerializeResponse(response));
});
}
internal static string GetResponse(string url, string userName, string password, Action<string> completed)
{
WebClient client = new WebClient();
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
client.Credentials = new NetworkCredential(userName, password);
}
try
{
client.DownloadStringCompleted += (s, args) =>
{
// Messy error handling needed here, out of scope
completed(args.Result);
};
client.DownloadStringAsync(new Uri(url));
}
catch
{
completed(null);
}
}
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
xboxManager.GetGamer( (xboxGamer) =>
{
// Need to move to the main UI thread.
Dispatcher.BeginInvoke(new Action<XboxGamer?>(DisplayGamerDetails), xboxGamer);
});
}
void DisplayGamerDetails(XboxGamer? xboxGamer)
{
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
As you can see async programming can get realy messy.
You generally have 2 options. Either you expose your backend code as an async API as well, or you need to wait for the call to complete in GetResponse.
Doing it the async way would mean starting the process one place, then return, and have the UI update when data is available. This is generally the preferred way, since calling a blocking method on the UI thread will make your app seem unresponsive as long as the method is running.
I think the "Silverlight Way" would be to use databinding. Your XboxGamer object should implement the INotifyPropertyChanged interface. When you call GetGamer() it returns immediately with an "empty" XboxGamer object (maybe with GamerTag=="Loading..." or something). In your ClientDownloadStringCompleted handler you should deserialize the returned XML and then fire the INotifyPropertyChanged.PropertyChanged event.
If you look at the "Windows Phone Databound Application" project template in the SDK, the ItemViewModel class is implemented this way.
Here is how you can expose asynchronous features to any type on WP7.