UploadStringAsync won't work in Windows Phone 8 - c#

I'm new in C# and I'd like to crate library for my RESTlike API in Windows Phone application.
My api structure is sort of
http://mysiteurl/api/method_name.json
So I would like to call api with (method_name, params) and return specified class.
When I launch this code on my Windows Phone 8.1 program freeze and nothing happens. Also I can see anything on serverside (any call)
API.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SampleProg.Model;
namespace SampleProg.Lib
{
public class Api
{
public async Task<VersionClass> versionInfo()
{
Debug.WriteLine("versionInfo()");
var result = await this.Post<VersionClass>("version", "{}", null);
Debug.WriteLine(result);
return result;
}
public async Task<TReturnType> Post<TReturnType>(string methodName, string data, Dictionary<string, string> headers)
{
var webClient = new WebClient();
webClient.Headers["Content-Type"] = "application/json";
webClient.Encoding = Encoding.UTF8;
var uri = new Uri(String.Format("http://mysiteurl/api/{0}.json", methodName));
if (headers != null)
{
foreach (var key in headers.Keys)
{
webClient.Headers[key] = headers[key];
}
}
return await Post<TReturnType>(webClient, uri, data);
}
private async Task<TReturnType> Post<TReturnType>(WebClient webClient, Uri uri, string jsonData)
{
TReturnType returnObject = default(TReturnType);
var taskCompletionSource = new TaskCompletionSource<TReturnType>();
webClient.UploadStringCompleted += (s, e) =>
{
var result = e.Result;
try
{
Debug.WriteLine(result);
returnObject = JsonConvert.DeserializeObject<TReturnType>(result);
taskCompletionSource.SetResult(returnObject);
}
catch (Exception ex)
{
var newEx = new Exception(
string.Format("Failed to deserialize server response: {0}", result), ex);
taskCompletionSource.SetException(newEx);
}
};
webClient.UploadStringAsync(uri, "POST", jsonData);
return await taskCompletionSource.Task;
}
}
}
App.xaml.cs (I call this from public App() {} ):
var api = new Api();
var apiTask = api.versionInfo();
VersionClass version = (VersionClass)apiTask.Result;
Debug.WriteLine(version.num);
VersionClass.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace SampleProg.Model
{
public class VersionClass
{
[JsonProperty("version")]
public int num { get; set; }
}
}
Could you tell me what is wrong? I use this code (at bottom) http://sandor68.rssing.com/chan-11557297/all_p1.html as base

This:
VersionClass version = (VersionClass) apiTask.Result;
Is deadlocking your app.
What's happening is the SynchronizationContext is trying to marshal control back to the UI thread after the await inside your inner method.
You should always await on a Task:
VersionClass version = (VersionClass) await apiTask;
Also, be aware that your wrapping of UploadStringAsync which returns a Task is already made for you OOTB via WebClient.UploadStringTaskAsync
Edit
If you choose to continue your version of Post, you should not await on the returned Task, you should let the user do that, and unregister the handler from the UploadStringCompleted:
private async Task<TReturnType> Post<TReturnType>(WebClient webClient, Uri uri, string jsonData)
{
TReturnType returnObject = default(TReturnType);
var taskCompletionSource = new TaskCompletionSource<TReturnType>();
UploadStringCompletedEventHandler handler = null;
handler = (s, e) =>
{
webClient.UploadStringCompleted -= handler;
var result = e.Result;
try
{
Debug.WriteLine(result);
returnObject = JsonConvert.DeserializeObject<TReturnType>(result);
taskCompletionSource.SetResult(returnObject);
}
catch (Exception ex)
{
var newEx = new Exception(
string.Format("Failed to deserialize server response: {0}", result), ex);
taskCompletionSource.SetException(newEx);
}
};
webClient.UploadStringCompleted += handler;
webClient.UploadStringAsync(uri, "POST", jsonData);
return taskCompletionSource.Task;
}

Related

API GET method doesn't store data in the property in Xamarin Forms

Data is being retrieve from the API succesfully, as I can see it here,
response
and then goes to the jsonstring, but never gets to the CantGet variable
I need it to be store in my property so I can use the value.
This is my API return:
[{"CantPremio":"70"}
Then this is my property:
using System;
using System.Collections.Generic;
using System.Text;
namespace ServLottery
{
public class GetCantPremio
{
public long CantPremio { get; set; }
}
}
This is the Get task
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
namespace ServLottery
{
public class RestClient
{
HttpClient client = new HttpClient();
public async Task<T> Get<T>(string URL)
{
try
{
var response = await client.GetAsync(URL);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var jsonstring = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonstring);
}
}
catch
{
}
return default(T);
}
}
}
Finally this is the call:
private async void GetCantDisponible()
{
try
{
RestClient client = new RestClient();
var CantGet = await client.Get<GetCantPremio>("https://servicentroapi.azurewebsites.net/api/GetNumber");
if (CantGet != null)
{
PremiosCantLocal = CantGet.CantPremio.ToString();
}
}
catch (Exception ex)
{
throw ex;
}
}
The api you are accessing is returning an array. So you must deserialize not a simple object but a list.
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonstring);
Replace the line that deserializes with this one. Should solve the problem
Like kelvin said, set the List<T> for the json array. And then foreach the collection to get the CantPremio.
RestClient:
public class RestClient
{
HttpClient client = new HttpClient();
public async Task<List<T>> Get<T>(string URL)
{
try
{
var response = await client.GetAsync(URL);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var jsonstring = await response.Content.ReadAsStringAsync();
var s = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonstring);
return s;
}
}
catch
{
}
return default(List<T>);
}
}
GetCantDisponible:
private async void GetCantDisponible()
{
try
{
RestClient client = new RestClient();
var CantGet = await client.Get<GetCantPremio>("https://servicentroapi.azurewebsites.net/api/GetNumber");
if (CantGet != null)
{
foreach (var item in CantGet)
{
var PremiosCantLocal = item.CantPremio.ToString();
}
}
}
catch (Exception ex)
{
throw ex;
}
}
Screenshot:
As mentioned, your API is returning an array but you're trying to deserialize it to a single instance. I'd suggest changing the call site of your client to pass a list for the type parameter:
List<GetCantPremio> CantGet = await client.Get<List<GetCantPremio>>("https://servicentroapi.azurewebsites.net/api/GetNumber");
Note that CantGet is now a List. If you are only looking for one object you could just add on a FirstOrDefault():
GetCantPremio CantGet = await client.Get<List<GetCantPremio>>("https://servicentroapi.azurewebsites.net/api/GetNumber")?.FirstOrDefault();

Get JSON from url, save to file, and finally CreateActivationService

Using the Template Studio extension for Visual Studio, I generated a project solution base and am now attempting to interject the app load process with a HTTP request before proceeding the render the page view.
App.xaml.cs
using System;
using Braytech_3.Services;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.UI.Xaml;
namespace Braytech_3
{
public sealed partial class App : Application
{
private Lazy<ActivationService> _activationService;
private ActivationService ActivationService
{
get { return _activationService.Value; }
}
public App()
{
InitializeComponent();
APIRequest();
// Deferred execution until used. Check https://msdn.microsoft.com/library/dd642331(v=vs.110).aspx for further info on Lazy<T> class.
_activationService = new Lazy<ActivationService>(CreateActivationService);
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
if (!args.PrelaunchActivated)
{
await ActivationService.ActivateAsync(args);
}
}
protected override async void OnActivated(IActivatedEventArgs args)
{
await ActivationService.ActivateAsync(args);
}
private async void APIRequest()
{
//Create an HTTP client object
Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
//Add a user-agent header to the GET request.
var headers = httpClient.DefaultRequestHeaders;
Uri requestUri = new Uri("https://json_url");
//Send the GET request asynchronously and retrieve the response as a string.
Windows.Web.Http.HttpResponseMessage httpResponse = new Windows.Web.Http.HttpResponseMessage();
string httpResponseBody = "";
try
{
//Send the GET request
httpResponse = await httpClient.GetAsync(requestUri);
httpResponse.EnsureSuccessStatusCode();
httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
APITempSave(httpResponseBody);
}
catch (Exception ex)
{
}
}
private async void APITempSave(string json)
{
StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
if (await tempFolder.TryGetItemAsync("APIData.json") != null)
{
StorageFile APIData = await tempFolder.GetFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
else
{
StorageFile APIData = await tempFolder.CreateFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
}
private ActivationService CreateActivationService()
{
return new ActivationService(this, typeof(Views.VendorsPage), new Lazy<UIElement>(CreateShell));
}
private UIElement CreateShell()
{
return new Views.ShellPage();
}
}
}
I think what I need to do is call _activationService = new Lazy<ActivationService>(CreateActivationService); once APITempSave() has been called but I am unsure of how to do so and what best practices are.
Any guidance would be greatly appreciated!
After further investigation and familiarisation with the generated solution, as well as additional Googling of await, async, and Tasks<>, I was able to implement the request as a service alongside items such as ThemeSelector, and ToastNotifications.
The ThemeSelector is one of the first things to be called in order to determine light and dark theme mode for the current user, so I was able to model my service around it and call it at the same time.
This is obviously very specific to the code that template studio generates, but some concepts are shared and should anyone else look for similar answers in the future maybe they'll find this.
APIRequest.cs (Service)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
namespace Braytech_3.Services
{
public static class APIRequest
{
internal static async Task Request()
{
//Create an HTTP client object
Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
//Add a user-agent header to the GET request.
var headers = httpClient.DefaultRequestHeaders;
Uri requestUri = new Uri("https://json_url");
//Send the GET request asynchronously and retrieve the response as a string.
Windows.Web.Http.HttpResponseMessage httpResponse = new Windows.Web.Http.HttpResponseMessage();
string httpResponseBody = "";
try
{
//Send the GET request
httpResponse = await httpClient.GetAsync(requestUri);
httpResponse.EnsureSuccessStatusCode();
httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
await APITempSave(httpResponseBody);
}
catch (Exception ex)
{
}
}
internal static async Task APITempSave(string json)
{
StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
if (await tempFolder.TryGetItemAsync("APIData.json") != null)
{
StorageFile APIData = await tempFolder.GetFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
else
{
StorageFile APIData = await tempFolder.CreateFileAsync("APIData.json");
await FileIO.WriteTextAsync(APIData, json);
}
}
}
}
ActiviationService.cs (originally called by App.xaml.cs)
private async Task InitializeAsync()
{
await ThemeSelectorService.InitializeAsync();
await APIRequest.Request();
}

Websockets using OWIN

All the examples using Microsoft WebSockets over a web-api that I've seen so far use IIS, the implementation is on the get method the HTTP connection is upgraded to a websocket and an instance of websocket handler is passed to the HTTPContext
public HttpResponseMessage Get() {
if (HttpContext.Current.IsWebSocketRequest) {
var noteHandler = new NoteSocketHandler();
HttpContext.Current.AcceptWebSocketRequest(noteHandler);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
What am trying to achieve is to do the same on an OWIN pipeline. The problem am facing is the connection is being upgraded to use Websockets but it is not utilizing my websocket handler. Where am I going wrong? Please suggest.
Controller utilizing OwinContext (Followed the example WebSockets in Nancy using OWIN),
public HttpResponseMessage Get() {
IOwinContext owinContext = Request.GetOwinContext();
WebSocketAccept acceptToken = owinContext.Get<WebSocketAccept>("websocket.Accept");
if (acceptToken != null) {
var requestHeaders = GetValue<IDictionary<string, string[]>>(owinContext.Environment, "owin.RequestHeaders");
Dictionary<string, object> acceptOptions = null;
string[] subProtocols;
if (requestHeaders.TryGetValue("Sec-WebSocket-Protocol", out subProtocols) && subProtocols.Length > 0) {
acceptOptions = new Dictionary<string, object>();
// Select the first one from the client
acceptOptions.Add("websocket.SubProtocol", subProtocols[0].Split(',').First().Trim());
}
acceptToken(acceptOptions, async wsEnv => {
var wsSendAsync = (WebSocketSendAsync)wsEnv["websocket.SendAsync"];
var wsRecieveAsync = (WebSocketReceiveAsync)wsEnv["websocket.ReceiveAsync"];
var wsCloseAsync = (WebSocketCloseAsync)wsEnv["websocket.CloseAsync"];
var wsCallCancelled = (CancellationToken)wsEnv["websocket.CallCancelled"];
//should I pass the handler to an event?
var handler = new NoteSocketHAndler();
});
} else {
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
Handler Code:
using System;
using Socket = Microsoft.Web.WebSockets;
using Newtonsoft.Json;
public class NoteSocketHandler : Socket.WebSocketHandler {
private static Socket.WebSocketCollection connections = new Socket.WebSocketCollection();
public NoteSocketHandler() {
}
public override void OnOpen() {
connections.Add(this);
}
public override void OnClose() {
connections.Remove(this);
}
public override void OnMessage(string message) {
ChatMessage chatMessage = JsonConvert.DeserializeObject<ChatMessage>(message);
foreach (var connection in connections) {
connection.Send(message);
}
}
}
I finally figured out how to resolve the issue. You can find the code below, also I've written a basic app which uses websockets on OWIN.
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Owin;
namespace NoteApp.WebService.Controller {
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NoteApp.WebService.Handler;
using WebSocketAccept = System.Action<
System.Collections.Generic.IDictionary<string, object>, // WebSocket Accept parameters
System.Func< // WebSocketFunc callback
System.Collections.Generic.IDictionary<string, object>, // WebSocket environment
System.Threading.Tasks.Task>>;
using WebSocketCloseAsync = System.Func<
int, // closeStatus
string, // closeDescription
System.Threading.CancellationToken, // cancel
System.Threading.Tasks.Task>;
using WebSocketReceiveAsync = System.Func<
System.ArraySegment<byte>, // data
System.Threading.CancellationToken, // cancel
System.Threading.Tasks.Task<
System.Tuple< // WebSocketReceiveTuple
int, // messageType
bool, // endOfMessage
int>>>; // count
// closeStatusDescription
using WebSocketReceiveResult = System.Tuple<int, bool, int>;
using WebSocketSendAsync = System.Func<
System.ArraySegment<byte>, // data
int, // message type
bool, // end of message
System.Threading.CancellationToken, // cancel
System.Threading.Tasks.Task>;
public class NoteController : ApiController {
public HttpResponseMessage Get() {
IOwinContext owinContext = Request.GetOwinContext();
WebSocketAccept acceptToken = owinContext.Get<WebSocketAccept>("websocket.Accept");
if (acceptToken != null) {
var requestHeaders = GetValue<IDictionary<string, string[]>>(owinContext.Environment, "owin.RequestHeaders");
Dictionary<string, object> acceptOptions = null;
string[] subProtocols;
if (requestHeaders.TryGetValue("Sec-WebSocket-Protocol", out subProtocols) && subProtocols.Length > 0) {
acceptOptions = new Dictionary<string, object>();
// Select the first one from the client
acceptOptions.Add("websocket.SubProtocol", subProtocols[0].Split(',').First().Trim());
}
acceptToken(acceptOptions, ProcessSocketConnection);
} else {
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
private async Task ProcessSocketConnection(IDictionary<string, object> wsEnv) {
var wsSendAsync = (WebSocketSendAsync)wsEnv["websocket.SendAsync"];
var wsCloseAsync = (WebSocketCloseAsync)wsEnv["websocket.CloseAsync"];
var wsCallCancelled = (CancellationToken)wsEnv["websocket.CallCancelled"];
var wsRecieveAsync = (WebSocketReceiveAsync)wsEnv["websocket.ReceiveAsync"];
//pass the sendasync tuple and the cancellation token to the handler. The handler uses the sendasync method to send message. Each connected client has access to this
var handler = new NoteSocketHandler(wsSendAsync, CancellationToken.None);
handler.OnOpen();
var buffer = new ArraySegment<byte>(new byte[100]);
try {
object status;
while (!wsEnv.TryGetValue("websocket.ClientCloseStatus", out status) || (int)status == 0) {
WebSocketReceiveResult webSocketResultTuple = await wsRecieveAsync(buffer, CancellationToken.None);
int count = webSocketResultTuple.Item3;
handler.OnMessage(Encoding.UTF8.GetString(buffer.Array, 0, count));
}
} catch (Exception ex) {
Console.WriteLine(ex.Message);
throw ex;
}
handler.OnClose();
await wsCloseAsync((int)WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
T GetValue<T>(IDictionary<string, object> env, string key) {
object value;
return env.TryGetValue(key, out value) && value is T ? (T)value : default(T);
}
}
}

Using await on HttpClient in wpf project

I just following a sample in calling HttpClient in sync mode, it works fine in a Console application.
However, when I move it to a wpf application, the program hanged without any return.
I try to isolate the problem by building a separate class to handle a dummy request to visit www.google.com.
It seems that the application hang in calling client.GetAsync, may I know if there has any thing need to be changed from console application to wpf in this case?
Please find the source of both the console application and wpf as below,
Console application - works fine:
using System;
using System.Threading.Tasks;
using System.Net.Http;
namespace ca03
{
static class action
{
static async Task<string> DownloadPageAsync()
{
// ... Target page.
string page = "http://www.google.com/";
// ... Use HttpClient.
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(page))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
// ... Display the result.
if (result != null &&
result.Length >= 50)
{
Console.WriteLine(result.Substring(0, 50) + "...");
}
return result;
}
}
public static string goDownload()
{
Task<string> x = DownloadPageAsync();
string result = x.Result;
return result;
}
}
class Program
{
static void Main(string[] args)
{
string data = action.goDownload();
Console.WriteLine(data);
Console.ReadLine();
}
}
}
WPF application: (just a plain project with a button added) - hang in GetAsync
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Net.Http;
namespace wpf02
{
static class action
{
static async Task<string> DownloadPageAsync()
{
// ... Target page.
string page = "http://www.google.com/";
// ... Use HttpClient.
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(page))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
return result;
}
}
public static string goDownload()
{
Task<string> x = DownloadPageAsync();
string result = x.Result;
return result;
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
string data = action.goDownload();
Console.WriteLine(data);
}
}
}
This line is the problem:
string result = x.Result;
When you use the Result property, that blocks the current thread until the task has completed. That's a problem when your continuations are meant to run on the same thread...
Basically, there's no point in your goDownload method - just change your DownloadPageAsync method to be public, then change your button_Click method to also be async so you can await the result:
private async void button_Click(object sender, RoutedEventArgs e)
{
string data = await action.DownloadPageAsync();
Console.WriteLine(data);
}
Top-level reason of this - WPF ( as any UI app) have a UI-thread, that work with winwdows loop queue. Jon Skeet give you a simplest desigion, but you can use thread from CLR ThreadPool:
Task<string> t = Task.Run(() => DownloadPageAsync());
string result = = await t;

HttpClient ReadAsync never returns

I've randomly issue with my code. I've list of 10 000 urls for testing and randomly it never finished, message "FINISH" isn't printed without any error/exception. I'm fighting with this issue 2 days without solution, any suggest?
Short code to reporduce the problem:
urlist for testing https://www.dropbox.com/s/0x6w1ej0fuxedw9/url.txt
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ProblemApp
{
static class AsyncForEach
{
public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
{
return Task.WhenAll(
from partition in Partitioner.Create(source).GetPartitions(dop)
select Task.Run(async delegate
{
using (partition)
while (partition.MoveNext())
await body(partition.Current);
}));
}
}
public class Form1 : Form
{
public Form1()
{
Load += OnLoad;
}
private void OnLoad(object sender, EventArgs eventArgs)
{
Go();
}
private async Task<bool> Open(string openurl)
{
Uri uri;
if (!Uri.TryCreate(openurl, UriKind.Absolute, out uri)) return false;
try
{
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(100));
using (var httpClient = new HttpClient())
{
using (var req = new HttpRequestMessage(HttpMethod.Get, new Uri(openurl)))
{
using (
HttpResponseMessage response =
await
httpClient.SendAsync(req, HttpCompletionOption.ResponseHeadersRead,
timeout.Token).ConfigureAwait(false))
{
if (response != null &&
(response.StatusCode == HttpStatusCode.OK ||
response.StatusCode == HttpStatusCode.NotFound))
{
using (
Stream responseStream =
await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
int read;
int offset = 0;
var rawResponse = new byte[8192];
while (
(read =
await
responseStream.ReadAsync(rawResponse, 0, 8192, timeout.Token)
.ConfigureAwait(false)) != 0)
{
offset += read;
if (offset > 1024000)
{
return false;
}
}
}
}
else
{
return false;
}
}
}
}
}
catch (WebException ex)
{
// Console.WriteLine(ex.Message);
} catch (HttpRequestException ex)
{
// Console.WriteLine(ex.Message);
}
return true;
}
private async void Go()
{
IEnumerable<string> lines = File.ReadLines("url.txt");
await
lines.ForEachAsync(500,
async line =>
{
bool result = await Open(line).ConfigureAwait(false);
});
Console.WriteLine("FINISH");
}
}
}
ForEachAsync from last example: http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx
With HttpCompletionOption.ResponseContentRead program is working OK

Categories

Resources