I have a class that goes and grabs some data and returns it as a string. While this object is working there is a spinning icon letting the user know work is being done. The problem is the code exits before the response comes back. I stuck a
while(response == null)
In just to see whats going on and the
response = (HttpWebResponse)request.EndGetResponse(AsyncResult);
is not firing. It fires ok in a console application so I am putting this down to something I am doing that silverlight doesn't like, heres the full code:
public class HttpWorker
{
private HttpWebRequest request;
private HttpWebResponse response;
private string responseAsString;
private string url;
public HttpWorker()
{
}
public string ReadFromUrl(string Url)
{
url = Url;
request = (HttpWebRequest)WebRequest.Create(url);
request.CookieContainer = new CookieContainer();
request.AllowAutoRedirect = true;
request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6";
AsyncRequest(); // The Demon!
return responseAsString;
}
private void AsyncRequest()
{
request.BeginGetResponse(new AsyncCallback(FinaliseAsyncRequest), null);
}
private void FinaliseAsyncRequest(IAsyncResult AsyncResult)
{
response = (HttpWebResponse)request.EndGetResponse(AsyncResult);
if (response.StatusCode == HttpStatusCode.OK)
{
// Create the stream, encoder and reader.
Stream responseStream = response.GetResponseStream();
Encoding streamEncoder = Encoding.UTF8;
StreamReader responseReader = new StreamReader(responseStream, streamEncoder);
responseAsString = responseReader.ReadToEnd();
}
else
{
throw new Exception(String.Format("Response Not Valid {0}", response.StatusCode));
}
}
}
Are you going into the busy loop with (while response == null) on the UI thread? The async callback for the HttpRequest will be delivered on the UI thread, so if you're looping on that same thread, the callback can never run. You need to return to allow the main message loop to run, and then your async callback will be delivered.
Your design above suggests that what you really want is a synchronous fetch anyway. Forget the callback and just call FinaliseAsyncRequest inside ReadFromUrl yourself. The UI will hang until the request completes, but it sounds like that's what you want.
I posted a working sample here of using WebClient and HttpWebRequest.
WebClient, HttpWebRequest and the UI Thread on Windows Phone 7
Note the latter is preferred for any non trivial processing to avoid blocking the UI.
Feel free to reuse the code.
The easiest way to get a string from a web server is to use WebClient.DownloadStringAsync() (MSDN docs).
Try something like this:
private void DownloadString(string address)
{
WebClient client = new WebClient();
Uri uri = new Uri(address);
client.DownloadStringCompleted += DownloadStringCallback;
client.DownloadStringAsync(uri);
StartWaitAnimation();
}
private void DownloadStringCallback(object sender, DownloadStringCompletedEventArgs e)
{
// Do something with e.Result (which is the returned string)
StopWaitAnimation();
}
Note that the callback executes on the UI thread and so you should only use this method if your callback method is not doing very much as it will block the UI while it executes.
If you need more control over the web request then you can use HttpWebRequest.
If you really must imitate synchronous behaviour have a look at Faking synchronous calls in Silverlight WP7
Related
If I have this code in Windows Phone 8, for instance
string __retS = null;
private String postRequest(String url, String postData)
{
byte[]byteData = Encoding.UTF8.GetBytes(postData);
HttpWebRequest request = null;
try
{
Uri uri = new Uri(url);
request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteData.Length;
// start the asynchronous operation
request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);
} // end try
catch (Exception)
{
}
return __retS;
}
I put a breakpoint on this line request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);. I expected that execution will jump to my GetRequestStreamCallback method but it doesn't. It rather goes on to execute the return statement hence a null value is always returned.
Is that how its supposed to go?
Is that how its supposed to go?
Yes. When the work is done it will invoke the callback function you passed. See "Asynchronous Programming Model (APM)". Starting with .Net 4.5 / c# 5.0, you can use async/await which can help to write async codes simpler.
var stream = await request.GetRequestStreamAsync();
//...do some work using that stream
The callback is executed asynchronously, that means the code is continued after the asynchronous method is assigned. (request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);)
When the WebRequest is finished, the GetRequestStreamCallback is executed.
Because the UI thread would be blocked if this request was synchronous the windows phone sdk only serves an asynchronous one.
I'm working on a Silverlight app which among other things makes Http requests in which it uploads a zip file from the web server. The zip file is picked up from the web server every n:th minute, a behavior controlled by a timer.
I've tried using the WebClient and HttpWebRequest classes with the same result. The request only reaches the web server the first time. The second, third, ..., time the request is sent and a response will occur. However, the request never reaches the web server...
void _timer_Tick(object sender, EventArgs e)
{
try
{
HttpWebRequest req = WebRequest.CreateHttp(_serverUrl + "channel.zip");
req.Method = "GET";
req.BeginGetResponse(new AsyncCallback(WebComplete), req);
}
catch (Exception ex)
{
throw ex;
}
}
void WebComplete(IAsyncResult a)
{
HttpWebRequest req = (HttpWebRequest)a.AsyncState;
HttpWebResponse res = (HttpWebResponse)req.EndGetResponse(a);
Stream stream = res.GetResponseStream();
byte[] content = readFully(stream);
unzip(content);
}
Is there some kind of browser caching issue here?
I want every request I make to go all the way to the web server.
Yes, the browser may be caching the request. If you want to disable that, you can either modify the server to send a Cache-Control: no-cache header, or you can append some sort of uniquifier to the URL that will prevent the browser from caching the request:
void _timer_Tick(object sender, EventArgs e)
{
try
{
HttpWebRequest req = WebRequest.CreateHttp(_serverUrl + "channel.zip?_=" + Environment.TickCount);
req.Method = "GET";
req.BeginGetResponse(new AsyncCallback(WebComplete), req);
}
catch (Exception ex)
{
throw ex;
}
}
Chances are your timer freezes, not the web request. Put a Debug.WriteLine in your timer event, make sure it gets called more than once.
It's also a bad idea to use timers for background tasks. Instead of a timer it's a better option to create a background task that sleeps between requests. This way even too long of server request won't cause calls to overlap.
Try something in the lines of:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork+=(s,a)=>{
try{
while (true)// or some meaningful cancellation condition is false
{
DownloadZipFile();
Sleep(FiveMinutes);
// don't update UI directly from this thread
}
} catch {
// show something to the user so they know automatic check died
}
};
worker.RunAsync();
How to get httpWebresponse in silverlight?
there is not method getResponse so this code doesn't work
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
And how to change this
...new StreamReader(resp.GetResponseStream(), Encoding.GetEncoding(1251)))
I've got error on 1251. What name of the encoding?
For the first one: you need to use the asynchronous version, since there are no synchronous networking calls in SL.
public void Button_Click(object sender, EventArgs e)
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
req.Method = "GET";
req.BeginGetResponse(HWRCallback, req);
}
void HWRCallback(IAsyncResult ar)
{
HttpWebRequest req = (HttpWebRequest)ar.AsyncState;
HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(ar);
// use response
}
For the second (by the way, consider asking two questions next time), maybe that encoding isn't supported in Silverlight. Loop through the result of Encoding.GetEncodings() to see all the encodings which are available in that platform.
Consider using the WebClient Class and in particular the DownloadStringAsync Method:
var client = new WebClient();
client.DownloadStringCompleted += (sender, e) =>
{
string result = e.Result;
};
client.DownloadStringAsync(uri);
It makes it simpler to perform an HTTP request as an asynchronous operation than HttpWebRequest. (In Silverlight, HTTP requests are required to be asynchronous.) And it provides the result conveniently as string, taking care of all encoding issues that might arise. (The server usually tells the client which encoding to use.)
I'm programming an application for Windows Phone 7. This application firstly sends, and then receives data from a server via HttpWebRequest. Most times it works fine, but sometimes, after receiving a portion of the data properly, I get a NullReferenceException in Stream.Read() function.
The communication starts when the user presses a button. Then I create the HttpWebRequest:
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(sUri);
request.Method = "POST";
request.BeginGetRequestStream(GetRequestStreamCallback, request);
The request callback method:
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
postStream = request.EndGetRequestStream(asynchronousResult);
this.bSyncOK = Send(); //This is my method to send data to the server
postStream.Close();
if (this.bSyncOK)
request.BeginGetResponse(GetResponseCallback, request);
else
manualEventWait.Set(); //This ManualResetEvent notify a thread the end of the communication, then a progressbar must be hidden
}
The response callback method:
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult) )
{
using (streamResponse = new StreamReader(response.GetResponseStream() ) )
{
this.bSyncOK = Recv(); //This is my generic method to receive the data
streamResponse.Close();
}
response.Close();
}
manualEventWait.Set(); //This ManualResetEvent notify a thread the end of the communication, then a progressbar must be hidden
}
And finally, this is the code where I get the exception reading the stream data:
int iBytesLeidos;
byte[] byteArrayUTF8 = new byte[8];
iBytesLeidos = streamResponse.BaseStream.Read(byteArrayUTF8, 0, 8); //NullReferenceException!!! -Server always send 8 bytes here-
When the application starts, I create a background thread that frequently sends info to the server. Background and Manual communications can run simultaneously. Could this be a problem?
Thanks.
If streamResponse is global variable, it can cause the problem in a case of an access from another thread. Pass your Stream to the Recv as a parameter
using (StreamReader streamResponse = new StreamReader(response.GetResponseStream() ) )
{
this.bSyncOK = Recv(streamResponse); //This is my generic method to receive the data
streamResponse.Close();
}
Where is your streamResponse declared in latter snippet? Is it the same object as in 3d snippet? Maybe you just use another variable, instead of actual stream.
in the second snippet, try to delete "postStream.Close();".
I have a web client I'm creating in Silverlight. I am trying to get it to communicate it with my web services on my server through GET and POST requests and JSON. The GET requests work fine and I'm able to parse the JSON on the Silverlight end. The POST requests however dont seem to work. The server reads that there is a POST request, but the POST array is empty.
Ive tried two pieces of code to send the POST requests, but both are resulting in the same response - an empty array.
The first Silverlight code I tried was:
public MainPage()
{
InitializeComponent();
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://www.dipzo.com/game/services.php"));
request.Method = "POST";
request.ContentType = "application/json";
request.BeginGetRequestStream(new AsyncCallback(OnGetRequestStreamCompleted), request);
}
private void OnGetRequestStreamCompleted(IAsyncResult ar)
{
HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
using (StreamWriter writer = new StreamWriter(request.EndGetRequestStream(ar)))
{
writer.Write("name=david");
}
request.BeginGetResponse(new AsyncCallback(OnGetResponseCompleted), request);
}
private void OnGetResponseCompleted(IAsyncResult ar)
{
//this.GetResponseCoimpleted.Visibility = Visibility.Visible;
// Complete the Flickr request and marshal to the UI thread
using (HttpWebResponse response = (HttpWebResponse)((HttpWebRequest)ar.AsyncState).EndGetResponse(ar))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string results = reader.ReadToEnd();
}
}
}
The second piece I tried was:
private void WebClient_Click(object sender, RoutedEventArgs e)
{
Test t1 = new Test() { Name = "Civics", Marks = 100 };
DataContractJsonSerializer jsondata = new DataContractJsonSerializer(typeof(Test));
MemoryStream mem = new MemoryStream();
jsondata.WriteObject(mem, t1);
string josnserdata = Encoding.UTF8.GetString(mem.ToArray(), 0, (int)mem.Length);
WebClient cnt = new WebClient();
cnt.UploadStringCompleted += new UploadStringCompletedEventHandler(cnt_UploadStringCompleted);
cnt.Headers["Content-type"] = "application/json";
cnt.Encoding = Encoding.UTF8;
cnt.UploadStringAsync(new Uri("http://www.dipzo.com/game/services.php"), "POST", josnserdata);
}
void cnt_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
var x = e;
}
The code on the server to consume the service is in PHP and is essentially:
var_dump($_POST)
This should output whatever is coming into the post array. I've tested it with a simple PHP client and it works. Just can't get it to work in silverlight. In silverlight I just keep getting an empty array.
You should change the Content-type to application/x-www-form-urlencoded and not application/json which is not a known type yet.
Not that I think anyone is still paying attention tot his old question, but I'm betting that the problem was that it actually WAS getting to the server, but that the server routed the result back to the SL application. This is the behavior I'm seeing with a similar situation from SL5 usingWebClient.UploadStringAsync.
I'm about to implement/test a technique I ran across yesterday which uses a dynamically built, "real" page post from SL; I'll report my findings shortly.
UPDATE -- THIS SOLUTION WORKS:
http://www.codeproject.com/Tips/392435/Using-HTTP-Form-POST-method-to-pass-parameters-fro
I've just tested it in my application (SL5 inside MVC) and it works just fine. Make sure you check the HttpContext.Request.Form["fieldname"] to get the value(s) that you want. I used this technique to submit JSON and was able to return a generated Word document for the user.
Once I implemented this I was able to get rid of the unnecessary WebClient that I was attempting to use before.