I've been struggling for a while now, but I just can't get this work. I'm trying to download a json string to my Windows Phone 8 application, by using the 'sort of' async await.
I'm using the promising solution of Matthias Shapiro.
HttpExtensions.cs
public static class HttpExtensions
{
public static Task<Stream> GetRequestStreamAsync(this HttpWebRequest request)
{
var taskComplete = new TaskCompletionSource<Stream>();
request.BeginGetRequestStream(ar =>
{
Stream requestStream = request.EndGetRequestStream(ar);
taskComplete.TrySetResult(requestStream);
}, request);
return taskComplete.Task;
}
public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest request)
{
var taskComplete = new TaskCompletionSource<HttpWebResponse>();
request.BeginGetResponse(asyncResponse =>
{
try
{
HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
taskComplete.TrySetResult(someResponse);
}
catch (WebException webExc)
{
HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
taskComplete.TrySetResult(failedResponse);
}
}, request);
return taskComplete.Task;
}
}
public static class HttpMethod
{
public static string Head { get { return "HEAD"; } }
public static string Post { get { return "POST"; } }
public static string Put { get { return "PUT"; } }
public static string Get { get { return "GET"; } }
public static string Delete { get { return "DELETE"; } }
public static string Trace { get { return "TRACE"; } }
public static string Options { get { return "OPTIONS"; } }
public static string Connect { get { return "CONNECT"; } }
public static string Patch { get { return "PATCH"; } }
}
And My MainPageViewModel.cs
protected override void OnActivate()
{
base.OnActivate();
GetSessions();
}
private async void GetSessions()
{
var result = await GetMyData("http://localhost/api/MyData");
}
public async Task<string> GetMyData(string urlToCall)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToCall);
request.Method = HttpMethod.Get;
HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
using (var sr = new StreamReader(response.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
Once it hits the "HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);", I'm getting a WebException:
"System.Net.WebException: The remote server returned an error: NotFound"
When I dive a bit deeper, I notice this error isn't the actual error. When I check the "asyncResponse" inside the GetResponseAsync method in the HttpExtensions class I notice the error:
"AsyncWaitHandle = 'asyncResponse.AsyncWaitHandle' threw an exception of type 'System.NotSupportedException'"
I have no idea how to get this work. Is it something I'm doing wrong?
I see the problem now. Since the emulator is using a virtual machine you cannot use localhost because localhost is the phone and not your PC. See this solution: Windows Phone 8 Emulator: Access localhost
Related
I'm using the following code to Validate URLs
private Boolean CheckURL(string url)
{
using (MyClient myclient = new MyClient())
{
try
{
myclient.HeadOnly = true;
// fine, no content downloaded
string s1 = myclient.DownloadString(url);
statusCode = null;
return true;
}
catch (WebException error)
{
if (error.Response != null)
{
HttpStatusCode scode = ((HttpWebResponse)error.Response).StatusCode;
if (scode != null)
{
statusCode = scode.ToString();
}
}
else
{
statusCode = "Unknown Error";
}
return false;
}
}
}
class MyClient : WebClient
{
public bool HeadOnly { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest req = base.GetWebRequest(address);
// req.Timeout = 3000;
if (HeadOnly && req.Method == "GET")
{
req.Method = "HEAD";
}
return req;
}
}
This works fine for most of the cases,but for some URLs it returns False Positive Results. For Valid URLs(when I browse using chrome) the method Returns
Not Found. Also for some URLs this method takes too much time to process.
What I'm doing wrong? Please advice..
UPDATE:
I'm checking the URLs from Multiple threads using Parallel,does this cause the problem?
public void StartThreads()
{
Parallel.ForEach(urllist, ProcessUrl);
}
private void ProcessUrl(string url)
{
Boolean valid = CheckURL(url);
this.Invoke((MethodInvoker)delegate()
{
if (valid)
{
//URL is Valid
}
else
{
//URL is Invalid
}
});
}
I'm starting the threads from a BackGround Worker to prevent UI Freezing
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
StartThreads();
}
Up until now, I've been making synchronous HttpWebRequest calls in WinForms applications. I want to start doing it asynchronously so as to not block the UI thread and have it hang. Therefore, I am attempting to switch to HttpClient, but I am also new to async and tasks and don't quite get it, yet.
I can launch the request and get a response and isolate the data I want (result, reasonPhrase, headers, code) but don't know how to get that back for display in textBox1. I also need to capture ex.message and return to the form if a timeout or cannot connect message occurs.
Every single example I see has the values written to Console.WriteLine() at the point they are available, but I need them returned back to the form for display and processing and have a hard time understanding how.
Here's a simple example:
namespace AsyncHttpClientTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "calling Test()...\r\n";
DownloadPageAsync();
// need to get from DownloadPageAsync here: result, reasonPhrase, headers, code
textBox1.AppendText("done Test()\r\n");
}
static async void DownloadPageAsync()
{
// ... Use HttpClient.
using (HttpClient client = new HttpClient())
{
try
{
using (HttpResponseMessage response = await client.GetAsync(new Uri("http://192.168.2.70/")))
{
using (HttpContent content = response.Content)
{
// need these to return to Form for display
string result = await content.ReadAsStringAsync();
string reasonPhrase = response.ReasonPhrase;
HttpResponseHeaders headers = response.Headers;
HttpStatusCode code = response.StatusCode;
}
}
}
catch (Exception ex)
{
// need to return ex.message for display.
}
}
}
}
}
Any helpful hints or advice?
create a model to hold the data you want to return
public class DownloadPageAsyncResult {
public string result { get; set; }
public string reasonPhrase { get; set; }
public HttpResponseHeaders headers { get; set; }
public HttpStatusCode code { get; set; }
public string errorMessage { get; set; }
}
Avoid using async void methods. Convert the method to async Task and call it in the event handler where it is allowed.
private async void button1_Click(object sender, EventArgs e) {
textBox1.Text = "calling Test()...\r\n";
var result = await DownloadPageAsync();
// Access result, reasonPhrase, headers, code here
textBox1.AppendText("done Test()\r\n");
}
static HttpClient client = new HttpClient();
static async Task<DownloadPageAsyncResult> DownloadPageAsync() {
var result = new DownloadPageAsyncResult();
try {
using (HttpResponseMessage response = await client.GetAsync(new Uri("http://192.168.2.70/"))) {
using (HttpContent content = response.Content) {
// need these to return to Form for display
string resultString = await content.ReadAsStringAsync();
string reasonPhrase = response.ReasonPhrase;
HttpResponseHeaders headers = response.Headers;
HttpStatusCode code = response.StatusCode;
result.result = resultString;
result.reasonPhrase = reasonPhrase;
result.headers = headers;
result.code = code;
}
}
} catch (Exception ex) {
// need to return ex.message for display.
result.errorMessage = ex.Message;
}
return result;
}
The HttpClientshould also not be created every time the download is called.
Refer to What is the overhead of creating a new HttpClient per call in a WebAPI client?
How can I do a GET using Windows Phone based on my Web API code? To make a Post i already know and Works for me, but i don't know to retrieve a list of Collections from my API, Thanks!
My API Code:
public class UsersController : ApiController
{
private ApiDatabaseEntities data = new ApiDatabaseEntities();
public IHttpActionResult GetUsuarios()
{
try
{
IEnumerable<Usuario> usuarios = data.Usuario.AsEnumerable();
return Ok(usuarios.AsEnumerable());
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
public IHttpActionResult PostUsuarios(Usuario usuario)
{
try
{
data.Usuario.Add(usuario);
data.SaveChanges();
return Ok();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
And here is my Windows phone code:
1 - WebApiBase
public abstract class WebApiBase<T>
{
private string Url = "http://localhost:63964/api/users";
protected virtual void Post(object objectPost)
{
HttpClient client = new HttpClient();
client.PostAsJsonAsync(Url, objectPost);
}
public abstract void Send(T objectPost);
}
2 - And UsuarioApi
public class UsuarioApi : WebApiBase<Models.UserPhone>
{
public override void Send(UserPhone objectPost)
{
this.Post(objectPost);
}
}
For Sending get request to server from Windows Phone and then handling the response from server.
public async void GetRequest(){ string url = "APIurl";
HttpClient _client = new HttpClient();
HttpResponseMessage _response = await _client.GetAsync(new Uri(url));
if (_response.IsSuccessStatusCode)
{
string APIResponse = await _response.Content.ReadAsStringAsync();
var myObject = Newtonsoft.Json.Linq.JArray.Parse(APIResponse).ToObject<MyClass>();
}
}
and MyClass
public class MyClass
{
public ObservableCollection<CollectionClass> _myCollection{get;set;}
}
public class CollectionClass
{
public string Name{get;set;}
}
and now You can perform operations on myObject as per your requirement.
I've implemented a command pattern in a project I'm working on. This is pretty much the current structure:
public class Response
{
public bool Success { get; private set; }
public static Response CreateErrorResponse()
{
return new Response { Success = false };
}
}
public interface ICommand<T> where T : Response
{
Task<T> ExecuteAsync();
}
public abstract CommandBase : ICommand<T> where T: Response
{
protected abstract Uri BuildUrl();
protected abstract Task<T> HandleResponseAsync();
public async override Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(url);
return await HandleResponseAsync(response);
}
}
I want to handle any exceptions that could be thrown by the HttpClient, so I want to change CommandBase.ExecuteAsync to something like this...
public async override Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new HttpClient();
try
{
var response = await httpClient.GetAsync(url);
return await HandleResponseAsync(response);
}
catch (HttpRequestException hex)
{
return Response.CreateErrorResponse(); // doesn't compile
}
}
The compile error I get is "Cannot convert type Response to async return type T". I can't use T.CreateErrorResponse(), as outlined in this question.
How can I work around this?
Edit to downvoters: whether or not you agree with catching exceptions in a library like this, the question still stands!
Although I am not sure this is the best solution (or feasible in your specific use case), what you can do is:
public class Response
{
public bool Success { get; private set; }
public ExceptionDispatchInfo ErrorInfo { get; private set; }
public bool HasFailed
{
get { return !Success; }
}
public static T CreateErrorResponse<T>(ExceptionDispatchInfo errorInfo) where T : Response, new()
{
var response = new T();
response.Success = false;
response.ErrorInfo = errorInfo;
return response;
}
}
Usage:
catch (HttpRequestException hex)
{
return Response.CreateErrorResponse<T>(ExceptionDispatchInfo.Capture(hex)); // should compile (I did not check)
}
You can cast the response to T. EDIT: Added full source code
public class Response
{
public bool Success { get; private set; }
public static Response CreateErrorResponse()
{
return new Response { Success = false };
}
}
public interface ICommand<T> where T : Response
{
Task<T> ExecuteAsync();
}
public abstract class CommandBase<T> : ICommand<T> where T: Response
{
protected abstract Uri BuildUrl();
protected abstract Task<T> HandleResponseAsync();
public async Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new System.Net.Http.HttpClient();
try
{
var response = await httpClient.GetAsync(url);
return null;// await HandleResponseAsync(response);
}
catch (Exception hex)
{
return (T)Response.CreateErrorResponse(); // doesn't compile
}
}
}
public async override Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new HttpClient();
try
{
var response = await httpClient.GetAsync(url);
return await HandleResponseAsync(response);
}
catch (HttpRequestException hex)
{
return (T)Response.CreateErrorResponse(); // compiles on liqpad
}
}
As Richard Willis suggests in http://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/ i'm trying to call a web request moking the behavior.
For that (I asking me if I'm messing something here) I implemented an IWebRequestCreate and extended a WebRequest and a WebResponse. (more details in link codes)
But now in my code I had a test that register (WebRequest.RegisterPrefix) a prefix:
[Test]
public void Test() {
var some = File.ReadAllBytes(#"TestData\WebService\admrond_13jan2011_14jan2011.xml");
WebRequest.RegisterPrefix("mockPrefix", new WebRequestCreateMock());
WebRequestFake request = WebRequestCreateMock.CreateRequestFake(some);
_remoteRepository.PopulateWithMeterData(_meter);
... (error in line before)
Then, I got this error: Invalid URI: The hostname could not be parsed.
But why? In my PopulateWithMeterData(Meter meter) I have this call:
WebRequest request = WebRequest.Create(urlListMeteringData);
WebResponse ws = request.GetResponse();
Some suggestion? Is interesting post my class implementations?
EDIT: as #Matthew ask:
public class WebRequestCreateMock : IWebRequestCreate {
static WebRequest _nextRequest;
static readonly object LockObject = new object();
static public WebRequest NextRequest {
get { return _nextRequest; }
set {
lock (LockObject) {
_nextRequest = value;
}
}
}
public WebRequest Create(Uri uri) {
return _nextRequest;
}
public static WebRequestFake CreateRequestFake(byte[] xmlStream) {
WebRequestFake webRequestFake = new WebRequestFake(xmlStream);
NextRequest = webRequestFake;
return webRequestFake;
}
}
public class WebRequestFake : WebRequest {
MemoryStream requestStream = new MemoryStream();
MemoryStream responseStream;
public override string Method { get; set; }
public override string ContentType { get; set; }
public override long ContentLength { get; set; }
public WebRequestFake(byte[] response) {
responseStream = new MemoryStream(response);
}
public override Stream GetRequestStream() {
return requestStream;
}
public override WebResponse GetResponse() {
return new WebReponseFake(responseStream);
}
}
public class WebReponseFake : WebResponse {
private readonly Stream _responseStream;
public WebReponseFake(Stream responseStream) {
_responseStream = responseStream;
}
public override Stream GetResponseStream() {
return _responseStream;
}
}
And the Url is something like: mockPrefix://NoMatterUrl
Since the error is "Invalid URI: The hostname could not be parsed." you are probably screwing up your Uri "mockPrefix://NoMatterUrl"
I had this problem once because I forgot to add a "/" between the domain uri and the request parameters.
Can you post exactly what your "NoMatterUri" looks like?
You need to register your prefix with a colon (':'); as in:
WebRequest.RegisterPrefix("mockPrefix:", new WebRequestCreateMock());
I have found that it's necessary to include a trailing "/" in the prefix. For instance, "test://localhost/":
[TestClass]
public class WebRequestTests
{
[TestMethod]
public void TestWebRequestCreate()
{
const string uriString = "test://localhost/foo/bar.baz?a=b&c=d";
var webRequest = new MockWebRequestCreateAssertUrl(uriString);
Assert.IsTrue(WebRequest.RegisterPrefix("test://localhost/", webRequest),
"Failed to register prefix");
Assert.IsNotNull(WebRequest.Create(uriString));
}
public class MockWebRequestCreateAssertUrl : IWebRequestCreate
{
private readonly Uri _expectedUri;
public MockWebRequestCreateAssertUrl(string uriString)
{
_expectedUri = new Uri(uriString);
}
public WebRequest Create(Uri uri)
{
Assert.AreEqual(_expectedUri, uri, "uri parameter is wrong");
return new MockWebRequestAssertUrl();
}
}
public class MockWebRequestAssertUrl : WebRequest {}
}