How an Asynchronous call communicate to UI - c#

A Windows Phone application referencing a dll(another class library project). There is an asynchronous webrequest in the dll to request a server and parse the response.
Click event of a button in the main page of WinPhone application calls the asynchronous method of referenced dll.The callback method raises an event when the response is received and parsed. Now when event raises I have the parsed object till HTTPcommunication layer. Simply how to show a message box with this result in the UI when HTTPcommunication module is done with its work.
public class HTTPRequester
{
public delegate void ResponseReceievedAndParsedDelegate(HTTPRequester eventRaiser, object result);
public event ResponseReceievedAndParsedDelegate ResponseReceivedAndParsed;
public void GetUserInformation(string userid)
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Credentials = new NetworkCredential("uid", "pwd");
request.Method = "GET";
request.Accept = "application/json";
object data = new object();
RequestState state = new RequestState(request, data);
IAsyncResult asr = request.BeginGetResponse(new AsyncCallback(RequesterCallback), state);
}
void RequesterCallback(IAsyncResult result)
{
RequestState state = (RequestState)result.AsyncState;
WebRequest request = (WebRequest)state.Request;
HttpWebResponse response =(HttpWebResponse)request.EndGetResponse(result);
Stream s = response.GetResponseStream();
StreamReader readStream = new StreamReader(s);
string dataString = readStream.ReadToEnd();
response.Close();
s.Close();
readStream.Close();
HTTPResponseParser grp = new HTTPResponseParser();
UserInfo ui = grp.ParseUserInformation(dataString );
state.Response = ui;
if (ResponseReceivedAndParsed != null)
{
ResponseReceivedAndParsed(this, ui);
}
}
}
(asynchronous)
MainUI------>HTTCommunicator--------->Server
MainUI HTTPCommunicator<--------Server
My problem is how to make the missing link to pass the response from HTTPCommunicator back to Main UI.
Hope I didn't confuse you guys.
Could anyone point to some code sample,if this can be done with Dispatcher.

In UI cerate method:
private void HandleMessage(HTTPRequester eventRaiser, object result)
{
UserInfo ui = (UserInfo)result;
//show message
}
After creating HttpRequester attach to its event:
HttpRequester xxx = new ...
xxx.ResponseReceivedAndParsed += new ResponseReceievedAndParsedDelegate(HandleMessage);

Related

Windows 8 Phone wait for BeginGetRequestStream to finish

I have the following code which im using to connect a REST
public void aquireToken()
{
HttpWebRequest apiR = (HttpWebRequest)HttpWebRequest.Create(new Uri(apiUrl + "authorize?"));
apiR.Method = "POST";
apiR.ContentType = "application/x-www-form-urlencoded";
apiR.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), apiR);
}
private void GetRequestStreamCallback(IAsyncResult callbackResult)
{
HttpWebRequest myRequest = (HttpWebRequest)callbackResult.AsyncState;
// End the stream request operation
Stream postStream = myRequest.EndGetRequestStream(callbackResult);
// Create the post data
string postData = "username=" + NTUser.username + "&appId=" + appId + "&signed=" + CreateSignedHex();
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Add the post data to the web request
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
// Start the web request
myRequest.BeginGetResponse(new AsyncCallback(GetResponsetStreamCallback), myRequest);
}
private void GetResponsetStreamCallback(IAsyncResult callbackResult)
{
HttpWebRequest request = (HttpWebRequest)callbackResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult);
using (StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream()))
{
string result = httpWebStreamReader.ReadToEnd();
var u = JsonConvert.DeserializeObject<dynamic>(result);
//string jsondata = u.data.toString();
NTUser.token = JsonConvert.DeserializeObject<Token>(u.data.ToString());
}
}
The problem is when calling the aquireToken method from the Phone UI, it doesn't wait on the httprequest to finish, which cause in nullreferences, because the next UI page is trying to access data which isn't loaded yet..
How do i present a simple loader on the UI thread while getting the needed information from the API?
Thanks in advance.
You have to wait for the code to complete. One way of doing that is passing another argument to acquireToken which will e called when the operation is done.
In that way, the UI calling the acquireRoken method knows to use the data only when the callbabl is called.
One way of showing a "loading UI" on your page is to place a 'Grid’ element with background of black or white that is semi transparent and has its visibility set to Collapsed. Place a progress bar in that grid. While loading, change the visibility of said grid to Visible, thus blocking the UI and letting your sea you are doing something.
public void DoUIThings()
{
// Do some UI related things.
acquireToken();
// Don't continue doing things here.... Wait for the ContinueDoUIThings() to be called.
}
public void ContinueDoUIThings()
{
// Now use your newly created token here...
// Do some UI related things.
// Note that this is called from below.
}
public void aquireToken()
{
// .... Your code was here..
apiR.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), apiR);
}
private void GetRequestStreamCallback(IAsyncResult callbackResult)
{
/// Your code was here.....
myRequest.BeginGetResponse(new AsyncCallback(GetResponsetStreamCallback), myRequest);
}
private void GetResponsetStreamCallback(IAsyncResult callbackResult)
{
HttpWebRequest request = (HttpWebRequest)callbackResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult);
using (StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream()))
{
string result = httpWebStreamReader.ReadToEnd();
var u = JsonConvert.DeserializeObject<dynamic>(result);
//string jsondata = u.data.toString();
NTUser.token = JsonConvert.DeserializeObject<Token>(u.data.ToString());
// Added this call here...
ContinueDoUIThings();
}
}

Getting data back from an HttpWebRequest.BeginGetResponse callback

I am writing a Windows Phone 8 app that is supposed to send an GET+POST request to a server and parse the answer.
The code I am using to send the request and to get a response back is the following (it is written in a separate static class):
// server to POST to
string url = "http://myserver.com/?page=hello&param2=val2";
// HTTP web request
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
// Write the request Asynchronously
using (var stream = await Task.Factory.FromAsync<Stream>(httpWebRequest.BeginGetRequestStream,
httpWebRequest.EndGetRequestStream, null))
{
// Create the post data
string postData = "pseudo=pseudo&titre=test&texte=\"Contenu du message\"";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Write the bytes to the stream
await stream.WriteAsync(byteArray, 0, byteArray.Length);
stream.Close();
IAsyncResult ar = httpWebRequest.BeginGetResponse(new AsyncCallback(GetResponsetStreamCallback), httpWebRequest);
}
}
private static void GetResponsetStreamCallback(IAsyncResult callbackResult)
{
HttpWebRequest request = (HttpWebRequest)callbackResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult);
using (StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream()))
{
string result = httpWebStreamReader.ReadToEnd();
//For debug: show results
System.Diagnostics.Debug.WriteLine(result);
}
My problem is : I have no idea how to get this answer (the string result) back in my behind-code in my app (or any other method in my app to be honest).
How could I do that?
You can try the following code,
string url = "http://myserver.com/?page=hello&param2=val2";
// HTTP web request
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
httpWebRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), webRequest);
}
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
using (var postStream = webRequest.EndGetRequestStream(asynchronousResult))
{
//send yoour data here
}
webRequest.BeginGetResponse(new AsyncCallback(GetResponseCallback), webRequest);
}
void GetResponseCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest myrequest = (HttpWebRequest)asynchronousResult.AsyncState;
using (HttpWebResponse response = (HttpWebResponse)myrequest.EndGetResponse(asynchronousResult))
{
System.IO.Stream responseStream = response.GetResponseStream();
using (var reader = new System.IO.StreamReader(responseStream))
{
data = reader.ReadToEnd();
}
responseStream.Close();
}
}
catch (Exception e)
{
//Handle Exception
}
else
throw;
}
}
public static string GetPageAsString(Uri address)
{
string result = "";
// Create the web request
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
// Get response
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream(), Constants.EncodingType);
// Read the whole contents and return as a string
result = reader.ReadToEnd();
}
return result;
}
Does it have to be a static class? Because if you have a new webrequest object for each request, then each response will come back into it's own object.v
You need to put the result somewhere that you can access it from the place you want to use it.
e.g. if you put it into another public static variable member then you can read it off where you need to. But you probably need to signal the UI to action it, or bind it to the UI.
If you use a static place to store it, then you will only have one active at a time. Unless you add it to a static list of items or results that you are working with
See also: http://blogs.msdn.com/b/devfish/archive/2011/04/07/httpwebrequest-fundamentals-windows-phone-services-consumption-part-2.aspx
You can: make a global variable in App.xaml.cs:
public string result;
In code use it as
(App.Current as App).result = httpWebStreamReader.ReadToEnd();
If you will need to get notified in your current active page when the result is updated - use delegates after you get the response which will signal to your page.

Fetching Result from HTTP Response

I have the following C# class which initiates an HTTP request from a Windows Phone to a server:
public class Request
{
public string data;
public string result;
public Request()
{
}
public void doRequest(string parameters, string URL)
{
data = parameters;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);
}
public void GetRequestStreamCallback(IAsyncResult callbackResult)
{
HttpWebRequest myRequest = (HttpWebRequest)callbackResult.AsyncState;
Stream postStream = myRequest.EndGetRequestStream(callbackResult);
byte[] byteArray = Encoding.UTF8.GetBytes(data);
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
myRequest.BeginGetResponse(new AsyncCallback(GetResponsetStreamCallback), myRequest);
}
public void GetResponsetStreamCallback(IAsyncResult callbackResult)
{
HttpWebRequest request = (HttpWebRequest)callbackResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult);
StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream());
result = httpWebStreamReader.ReadToEnd();
}
Now, in my main class, I call the doRequest method to do an HTTP request from the Windows Phone:
Request req = new Request();
req.doRequest("function=LogIn&username=" + username + "&password=" + password, "http://localhost:4000/Handler.ashx");
When calling this method, how can I get the result (the result variable) from the server since it is received in the GetResponsetStreamCallback method and not in the doRequest method?
You have several possibilities. One would be to define property to make the result accessible outside. Define
public class Request
{
public string Result
{
get{
if(result != null && !string.IsNullOrEmpty(result))
return result;
return null;
}
}
...
}
You might create an event and have objects subscribe to it, so you can notice them when the asynchronous request has ended. Or make your calls synchronous, which is a little easier to do, as you don't have synchronize your calls and the requests from other objects.

How do I actually get the response of an async web request from outside the method?

I'm a little confused. I'm trying to post to my web service in an async manner, ideally I want to start the request, show a loading spinner on the UI and then when the async request finishes process the response and either show an error if there is one, or do another operation with the result.
Here is my code, I call the request here and pass some data in.
private void SignInExecute()
{
if (Username == null || Password == null)
{
LoginOutput = "Please provide a username or password.";
}
else
{
this.webService.SendLoginRequest("http://localhost:3000/client_sessions", "username=" + Username + "&password=" + Password);
}
}
And here is the actual web request code:
public void SendLoginRequest(string url, string postdata)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.Accept = "application/json";
byte[] byteArray = Encoding.UTF8.GetBytes(postdata);
request.CookieContainer = new CookieContainer();
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
((HttpWebRequest)request).KeepAlive = false;
request.BeginGetResponse(new AsyncCallback(GetLoginResponseCallback), request);
}
private static void GetLoginResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
Console.WriteLine(responseString);
// Close the stream object
streamResponse.Close();
streamRead.Close();
response.Close();
}
So to sum up. I want to be able to return the response back to the object which originally gave the call for the web request to start. Any help?
You need to tell the BeginGetResponse to go back to the same context in which it was called via SynchronizationContext.Current. Something like this (the code does not have proper error checking, so you should think about that properly) (Also, Platinum Azure is correct that you should use a using to let your streams close properly (and guaranteed):
In your SendLoginRequest:
//Box your object state with the current thread context
object[] boxedItems = new []{request, SynchronizationContext.Current};
request.BeginGetResponse(new AsyncCallback(GetLoginResponseCallback),
boxedItems);
The getresponse code:
private static void GetLoginResponseCallback(IAsyncResult asynchronousResult)
{
//MY UPDATE
//Unbox your object state with the current thread context
object[] boxedItems = asynchronousResult.AsyncState as object[];
HttpWebRequest request = boxedItems[0] as HttpWebRequest;
SynchronizationContext context = boxedItems[1] as SynchronizationContext;
// End the operation
using(HttpWebResponse response =
(HttpWebResponse)request.EndGetResponse(asynchronousResult))
{
using(Stream streamResponse = response.GetResponseStream())
{
using(StreamReader streamRead = new StreamReader(streamResponse))
{
string responseString = streamRead.ReadToEnd();
Console.WriteLine(responseString);
//MY UPDATE
//Make an asynchronous call back onto the main UI thread
//(context.Send for a synchronous call)
//Pass responseString as your method parameter
//If you have more than one object, you will have to box again
context.Post(UIMethodToCall, responseString);
}
}
}
}
To implement your UI processing
public static void UIMethodCall(object ObjectState)
{
String response = ObjectState as String;
label1.Text = String.Format("Output: {0}", response);
//Or whatever you need to do in the UI...
}
Now, I would test this out first, though. My understanding of Microsoft's implementation of event driven async was that the response was context-aware, and knew which context to return to. So, before jumping to the assumption that you are not on the same context, test it out by trying to update the UI (this will cause a thread context exception if you are not on the calling (UI) thread)

Why isn't the BeginGetResponse callback being called?

Here's my code:
namespace RequestApi
{
public partial class MainPage : PhoneApplicationPage
{
private BackgroundWorker bw;
private string ans;
private JObject ansJson;
private static ManualResetEvent allDone = new ManualResetEvent(false);
// Constructor
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
string url = "http://192.168.0.43:8182/Account/SignIn";
CreateRequest(url);
userId.Text = ansJson.Value<int>("user_id").ToString();
}
private void CreateRequest(string url)
{
Debug.WriteLine("CreateRequest");
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
req.ContentType = "application/json";
req.Method = "POST";
req.BeginGetRequestStream(new AsyncCallback(RequestCallback), req);
allDone.WaitOne();
}
private void RequestCallback(IAsyncResult aresult)
{
Debug.WriteLine("RequestCallback");
HttpWebRequest req = (HttpWebRequest)aresult.AsyncState;
Stream postStream = req.EndGetRequestStream(aresult);
string obj = "{ 'username': 'test_2#aragast.com', 'password': 'a123456' }";
JObject json = JObject.Parse(obj);
string s = JsonConvert.SerializeObject(json);
byte[] postdata = System.Text.Encoding.Unicode.GetBytes(s);
postStream.Write(postdata, 0, postdata.Length);
postStream.Close();
req.BeginGetResponse(new AsyncCallback(ResponseCallback), req);
}
private void ResponseCallback(IAsyncResult aresult)
{
Debug.WriteLine("ResponseCallback");
HttpWebRequest req = (HttpWebRequest)aresult.AsyncState;
HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(aresult);
StreamReader reader = new StreamReader(resp.GetResponseStream());
string response = reader.ReadToEnd();
Debug.WriteLine(response);
JObject responseJson = JObject.Parse(response);
ansJson = responseJson;
Debug.WriteLine("ansJson from responseCallback {0}", ansJson);
reader.Close();
resp.Close();
allDone.Set();
}
}
}
When I debug application it entered CreateRequest then it enter RequestCallback, but then it stop and never enter ResponseCallback instead it tryis userId.Text to assign asnJson value which is null, because it doesn't enter ResponseCallback. When I do wrong, and why it never enter ResponseCallback?
Your ManualResetEvent is created with true as the argument, so it's already signalled to start with. That means the allDone.Wait() call will immediately continue... so CreateRequest will finish, and you'll immediately try to use the asnJson variable, which as you've said is null. That will kill the application, I suspect - so you never get a chance to get a response.
Now, the solution to this is not to change the ManualResetEvent constructor - you shouldn't be waiting like that in the UI thread anyway! You'll block it, and you've removed the whole point of Windows Phone 7 making everything asynchronous to start with.
Instead, your callbacks should use the dispatcher to call back into the UI thread when they've finished. Note that you should also have robust error handling in case anything goes wrong with the request.
Not exactly related to answering the question, but an answer block seems to be the only place to fit the recommendation I'm about to make... You should have appropriate resource protection around your streams by employing using blocks as such:
Original code:
Stream postStream = req.EndGetRequestStream(aresult);
string obj = "{ 'username': 'test_2#aragast.com', 'password': 'a123456' }";
JObject json = JObject.Parse(obj);
string s = JsonConvert.SerializeObject(json);
byte[] postdata = System.Text.Encoding.Unicode.GetBytes(s);
postStream.Write(postdata, 0, postdata.Length);
postStream.Close();
New code (also make sure your encoding is correct for the request: is your web service really expecting UTF-16? It's more common that web servers use UTF-8 (Encoding.UTF8)) :
using (Stream postStream = req.EndGetRequestStream(aresult))
{
string obj = "{ 'username': 'test_2#aragast.com', 'password': 'a123456' }";
JObject json = JObject.Parse(obj);
string s = JsonConvert.SerializeObject(json);
byte[] postdata = System.Text.Encoding.Unicode.GetBytes(s);
postStream.Write(postdata, 0, postdata.Length);
}
Original code:
HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(aresult);
StreamReader reader = new StreamReader(resp.GetResponseStream());
string response = reader.ReadToEnd();
Debug.WriteLine(response);
JObject responseJson = JObject.Parse(response);
ansJson = responseJson;
Debug.WriteLine("ansJson from responseCallback {0}", ansJson);
reader.Close();
resp.Close();
New code:
using (HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(aresult))
using (StreamReader reader = new StreamReader(resp.GetResponseStream()))
{
string response = reader.ReadToEnd();
Debug.WriteLine(response);
JObject responseJson = JObject.Parse(response);
ansJson = responseJson;
Debug.WriteLine("ansJson from responseCallback {0}", ansJson);
}
I would also recommend surrounding your req and resp operations with try..catch blocks to allow something to process exceptional conditions -- otherwise it will bubble up to the AppDomain's exception handler (can also be hooked by the UnhandledException event).

Categories

Resources