How to write an asynchronous method? - windows-phone-7

I have a method that performs Http POST, and since I'm using HttpWebRequest to perform it, the method relies on asynchronous calls. Since I need my method to return the response code of my Http POST, I want to make my method asynchronous. How do I do this?
I was thinking of using Dispatcher.
EDIT: So a basic outline of the structure of my code looks like this:
string response;
string httpPost(){
HttpWebRequest.BeginGetRequestStream(new AsyncCallback(requestCallback), httpWebRequest);
return response;
}
void requestCallback(IAsyncResult asyncResult){
HttpWebRequest.EndGetRequestStream(asyncResult);
HttpWebRequest.BeginGetResponse(new AsyncCallback(responseCallback), httpWebRequest);
}
void responseCallback(IAsyncResult asyncResult){
HttpWebResponse webResponse = (HttpWebResponse) HttpWebRequest.EndGetResponse(asyncResult);
response = webResponse.StatusCode.ToString();
}
I want to change httpPost() to an asynchronous method.
EDIT2:
public static void httpPost(Action<string> completed)
{
HttpWebRequest.BeginGetRequestStream(new AsyncCallback(requestCallback), httpWebRequest);
completed(HttpEngine.response);
}

On WP7, HTTPWebRequest will already be asynchronous - for an example of its use, see this code from http://www.rudigrobler.net/blog/wp7-webclient-vs-httpwebrequest
public void DoThePost(Action<string> onSuccess)
{
var request = (HttpWebRequest)WebRequest.Create(new Uri("http://www.sherdog.com/rss/news.xml"));
request.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
onSuccess(response);
}));
}
}, request);
}
Called with:
DoPost((responseText) => { responseTextBlock.Text = responseText;});

Related

Get HttpHeaders from HttpRequestException?

I have a Web API, When the incoming request is not valid then the API sends back a HttpStatusCode.BadRequest and API would also add a CorrelationId into Response's HttpHeader. Something like below
public class ValidateRequestAttribute : ActionFilterAttribute
{
public ValidateRequestAttribute()
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid == false)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.HttpContext.Response.Headers.Add("x-correlationid", "someid");
context.Result = new ContentResult()
{
Content = "bad request."
};
}
}
}
On client side im using HttpClient to access the API. I am not sure how client would retrieve HttpStatusCode and HttpHeader here. Here is my client code
public bool Process(url)
{
bool result = false;
try
{
Task.Run(async () => await _httpClient.GetStringAsync(url).ConfigureAwait(false)).Result;
}
catch (Exception ex)
{
if(ex is AggregateException)
{
var aggregateException = ex as AggregateException;
foreach(var innerException in aggregateException.InnerExceptions)
{
if (innerException is HttpRequestException)
{
var httpRequestException = innerException as HttpRequestException;
// how do i get StatusCode and HttpHeader values here??
}
}
}
}
return result;
}
I have already gone through SO post here and MSDN article here and also Stephen Cleary's article here
Even though its recommended to make async all the way down, I this case Client and API are both disconnected from each other and client is synchronous. Note that Client's Process method is synchronous method.
Like this:
public bool Process(string url)
{
var result = _httpClient.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
if (result.StatusCode == HttpStatusCode.BadRequest)
{
IEnumerable<string> values;
if (result.Headers.TryGetValues("x-correlationid", out values))
{
// Should print out "someid"
Console.WriteLine(values.First());
}
}
return result.IsSuccessStatusCode;
}
Also note that doing .GetAwaiter().GetResult(); vs .Result; is recommended since it makes the code easier to work with because it does not throw an AggregateException.
If you want to read the response content as a string just do:
var content = result.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
If you want to make your code async though you should use the async/await keyword and skip the .GetAwaiter().GetResult();.

HttpWebRequest inside a custom class

I'm trying to make an app for windows phone 7 mango, to parse the content of a website. I managed to write all the code, but it was like a war zone ;). When i tried to rearrange the code in a better way, i start facing a very strange problem.
The problem is when i made a custom class called "MedinetMySchedule" inside my project and use breakpoint to step through this class. I found out that the app steps throught the first method getrequest() then return back to the mainpage for few steps, then returns back to the second methods GetRequestStreamCallback(). This jump to the mainpage leaves me with a string having a value of null to parse. Then it jump back to the third method ReadWebRequestCallback(). This last jump gives me nothing important as the debugg ends and i get nothing on the phone-emulator. The MedinetMySchedule class has the following code:-
namespace WindowsPhonePanoramaApplication1
{
public class MedinetMySchedule
{
//Medinet user url
public string url { get; set; }
public String myresponse;
public void getrequest()
{
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.BeginGetRequestStream(GetRequestStreamCallback, request);
}
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
using (Stream postStream = request.EndGetRequestStream(asynchronousResult))
{
string postData = string.Format("username={0}&password={1}&customer=******&doLogin=Logga+in&language=se", "*****", "******");
// Convert the string into a byte array.
byte[] data = Encoding.UTF8.GetBytes(postData);
// Write to the request stream.
postStream.Write(data, 0, data.Length);
postStream.Close();
}
//Initiating get response
request.BeginGetResponse(ReadWebRequestCallBack, request);
}
private void ReadWebRequestCallBack(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
WebResponse myResponse = (WebResponse)request.EndGetResponse(asynchronousResult);
Stream encodingStream = myResponse.GetResponseStream();
Encoding encode = Encoding.GetEncoding("iso-8859-1");
using (StreamReader httpwebStreamReader = new StreamReader(encodingStream, encode))
{
myresponse= httpwebStreamReader.ReadToEnd();
}
myResponse.Close();
}
}
}
Here is the code that calls the getrequest() and parses the content:-
MedinetMySchedule mittschema = new MedinetMySchedule();
mittschema.url = "https://medinet.se/cgi-bin/doctor.pl?action=login&customer=saskir&language=se";
mittschema.getrequest();
Dispatcher.BeginInvoke(() => parseResults(mittschema.myresponse));
private void parseResults(string myresponse)
{
if (string.IsNullOrEmpty(myresponse))
{
return;
}
//Initiating a listbox and add item to it
List<MediNetScheme> medinetScheme = new List<MediNetScheme>();
//Using HtmlAgilityPack to parse the HTMLcode from the response
HtmlDocument htdoc = new HtmlDocument();
htdoc.LoadHtml(myresponse);
foreach (HtmlNode table in htdoc.DocumentNode.SelectNodes("//table[#class='list-medium']/tbody[1]/tr[#class]"))
{
//Removing ChildNode
table.ChildNodes.RemoveAt(3);
string itemValue = table.InnerText;
//Changing the parsed date into a DateTime
string d;
DateTime datum = DateTime.Parse(itemValue.Remove(11));
d = datum.ToString("D");
//Adding items to the listbox
medinetScheme.Add(new MediNetScheme() { Datum = d, Sections = itemValue.Remove(0, 15) });
}
MediNetScheme.ItemsSource = medinetScheme;
}
Any ideas why this is happening and how to correct it?
Yours
/Omar
The execution going back and forth is because request.BeginGetRequestStream is asynchronous. Basically, it creates a new thread, and executes in parallel of your main code. Therefore, you end up calling parseResult before you've finished downloading the data. There's many ways to rewrite your code, my favorite is using a callback:
First, change the getRequest method to accept a callback, and store it:
private Action Callback;
public void getrequest(Action callback)
{
this.Callback = callback;
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.BeginGetRequestStream(GetRequestStreamCallback, request);
}
Then, at the end of ReadWebRequestCallback, invoke this callback:
private void ReadWebRequestCallBack(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
WebResponse myResponse = (WebResponse)request.EndGetResponse(asynchronousResult);
Stream encodingStream = myResponse.GetResponseStream();
Encoding encode = Encoding.GetEncoding("iso-8859-1");
using (StreamReader httpwebStreamReader = new StreamReader(encodingStream, encode))
{
myresponse= httpwebStreamReader.ReadToEnd();
}
myResponse.Close();
this.Callback();
}
Finally, from mainpage, call the getrequest method and tell it to use parseResults as callback:
Action callback = () => Dispatcher.BeginInvoke(() => parseResults(mittschema.myresponse));
mittschema.getrequest(callback);

how to perform post method in windows 8 metro?

I have followed the HttpClient samples but couldn't figure it out how to post a method with 2 parameters.
Below is what I tried but it return bad gateway error:
private async void Scenario3Start_Click(object sender, RoutedEventArgs e)
{
if (!TryUpdateBaseAddress())
{
return;
}
Scenario3Reset();
Scenario3OutputText.Text += "In progress";
string resourceAddress = "http://music.api.com/api/search_tracks";
try
{
MultipartFormDataContent form = new MultipartFormDataContent();
// form.Add(new StringContent(Scenario3PostText.Text), "data");
form.Add(new StringContent("Beautiful"), "track");
form.Add(new StringContent("Enimem"), "artist");
HttpResponseMessage response = await httpClient.PostAsync(resourceAddress, form);
}
catch (HttpRequestException hre)
{
Scenario3OutputText.Text = hre.ToString();
}
catch (Exception ex)
{
// For debugging
Scenario3OutputText.Text = ex.ToString();
}
}
I looked all over the internet, but couldn't find any working examples or documents that show how to perform the http post method. Any materials or samples would help me a lot.
Try FormUrlEncodedContent instead of MultipartFormDataContent:
var content = new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("track", "Beautiful"),
new KeyValuePair<string, string>("artist", "Enimem")
}
);
I prefer to take the following approach where you set the POST data into the request content body. Having to debug it is much easier!
Create your HttpClient object with the URL you're posting to:
string oauthUrl = "https://accounts.google.com/o/oauth2/token";
HttpClient theAuthClient = new HttpClient();
Form your request with the Post method to your url
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, oauthUrl);
Create a content string with your parameters explicitly set in POST data format and set these in the request:
string content = "track=beautiful" +
"&artist=eminem"+
"&rating=explicit";
request.Method = HttpMethod.Post;
request.Content = new StreamContent(new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)));
request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
Send the request and get a response:
try
{
HttpResponseMessage response = await theAuthClient.SendAsync(request);
handleResponse(response);
}
catch (HttpRequestException hre)
{
}
Your handler will be called once the request returns and will have response data from your POST. The following example shows a handler that you could put a breakpoint into to see what the response content is, at that point, you could parse it or do whatever you need to do with it.
public async void handleResponse(HttpResponseMessage response)
{
string content = await response.Content.ReadAsStringAsync();
if (content != null)
{
// put your breakpoint here and poke around in the data
}
}

Return response from HTTPWebRequest somehow?

I'm trying to validate a user in my WP7 app by validating username/pass on a server.
In my "validate user" method I create a HTTPWebRequest with the stuff I need validated.
Then as far as I can see, the only option to execute is to use request.BeginGetResponse, with an async callback.
But I want to return the response from the request in the same method that I created the request in, how can I accomplish this?
I'm not sure why you would like to make it return in the calling method. I would just use the standard WebClient behavior like below:
public void ValidateUser()
{
WebClient webClient = new WebClient();
Uri uri = new Uri(url);
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri(url));
}
void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
//Validate result from response by using e.Result
}
BeginGetResponse returns an IAsyncResult which has a wait handle that is signalled when the request completes. All you have to do is wait on this handle: [ doesn't work on WP7 ]
UPDATE: use a ManualResetEvent
var mre = new ManualResetEvent( false );
var iar = myHttpWebRequest.BeginGetResponse( state => mre.Set(), null );
mre.WaitOne();
var response = myHttpWebRequest.EndGetResponse( iar );
The only real way to achieve what you need would be to create an event in your worker class, and fire that event when the async call completes, so (pseudocode)...
void StartDownload(object[] parameters)
{
var req = HttpWebRequest.Create("http://google.com");
req.BeginGetRequestStream(Completed_handler, req);
}
void Completed_handler(object sender, DownloadStringCompletedEventArgs e)
{
var request = (HttpWebRequest)result.AsyncState;
var response = request.EndGetResponse(result);
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
var contents = reader.ReadToEnd();
NotifyCallerOfContent(contents)
}
}
public event EventHandler<MyCustomEventArgsClass> DownloadHasFinished;
public NotifyCallerOfDownload(string content)
{
if(null != DownloadHasFinished)
{
DownloadHasFinished(this, new MyCustomEventArgsClass(content));
}
}
and then subscribe to the DownloadHasFinished event in your calling class.
As for calling out specifically by a HttpWebRequest, take a look at this question, where you will find some working samples.

how to make HTTP POST using reactive extension on windows phone 7

I found an example about HTTP POST in msdn, but I am wondering how can I make use of reactive extensions here.
using System;
using System.Net;
using System.IO;
using System.Text; using System.Threading;
class HttpWebRequestBeginGetRequest
{
private static ManualResetEvent allDone = new ManualResetEvent(false);
public static void Main(string[] args)
{
// Create a new HttpWebRequest object.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.contoso.com/example.aspx");
request.ContentType = "application/x-www-form-urlencoded";
// Set the Method property to 'POST' to post data to the URI.
request.Method = "POST";
// start the asynchronous operation
request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);
// Keep the main thread from continuing while the asynchronous
// operation completes. A real world application
// could do something useful such as updating its user interface.
allDone.WaitOne();
}
private static void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
Stream postStream = request.EndGetRequestStream(asynchronousResult);
Console.WriteLine("Please enter the input data to be posted:");
string postData = Console.ReadLine();
// Convert the string into a byte array.
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Write to the request stream.
postStream.Write(byteArray, 0, postData.Length);
postStream.Close();
// Start the asynchronous operation to get the response
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
private static void GetResponseCallback(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();
// Release the HttpWebResponse
response.Close();
allDone.Set();
}
}
I am trying to use the following code, but it does not work. Can anyone help me out on this?
Thanks in advance -Peng
return (from request in
Observable.Return((HttpWebRequest)WebRequest.Create(new Uri(postUrl))).Catch(Observable.Empty<HttpWebRequest>())
.Do(req =>
{
// Set up the request properties
req.Method = "POST";
req.ContentType = contentType;
req.UserAgent = userAgent;
req.CookieContainer = new CookieContainer();
Observable.FromAsyncPattern<Stream>(req.BeginGetRequestStream, req.EndGetRequestStream)()
.ObserveOnDispatcher()
.Subscribe(stream =>
{
stream.Write(formData, 0,
formData.Length);
stream.Close();
})
;
})
from response in
Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)().Catch(Observable.Empty<WebResponse>())
from item in GetPostResponse(response.GetResponseStream()).ToObservable().Catch(Observable.Empty<string>())
select item).ObserveOnDispatcher();
Edit: To make it clear, I want to use the rx to implement the same logic in MSDN example.
in the MSDN example, it seems it first makes async call to write RequestStream, and then in the GetRequestStreamCallback, fires another async call to get the response.
Using Rx, I am able to create 2 observables
1. Observable.FromAsyncPattern(request.BeginGetRequestStream, request.EndGetRequestStream)()
2. Observable.FromAsyncPattern(request.BeginGetResponse, request.EndGetResponse)()
The problem is the second observable depends on the first one's result, so how can I do this in Rx?
In the first observable's subcribe method to create the seond observable? is it the good way?
This is how I am doing it. I configure the two Async patters up front, then use SelectMany to chain them together.
I have cut out the error handling etc from this code to keep it simple and show only the bare minimum to get it working. You should append a .Catch() similar to your own code, and if you want to get more than just a string out (say the response code) then you'll need to create a class/struct to hold all the bits of data you need and return that instead.
public IObservable<string> BeginPost(Uri uri, string postData) {
var request = HttpWebRequest.CreateHttp(uri);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
var fetchRequestStream = Observable.FromAsyncPattern<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream);
var fetchResponse = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse);
return fetchRequestStream().SelectMany(stream => {
using (var writer = new StreamWriter(stream)) writer.Write(postData);
return fetchResponse();
}).Select(result => {
var response = (HttpWebResponse)result;
string s = "";
if (response.StatusCode == HttpStatusCode.OK) {
using (var reader = new StreamReader(response.GetResponseStream())) s = reader.ReadToEnd();
}
return s;
});
}
Your problem is your use of Do() here, you need to move the GetRequestStream into your SelectMany (into your "from bla in, from bla in"...), since it only makes sense to get the response stream after you've written the full request. Right now, you're trying to do both concurrently.

Resources