需要使用HttpWebRequest在WP8中进行同步REST调用

时间:2013-04-29 19:26:43

标签: c# rest windows-phone-8 windows-phone

我已经查看了类似问题的一些答案,似乎找不到适合我正在做的事情。我需要使用HttpWebRequest做一些同步请求(一些使用每个动词,GET / PUT / POST / DELETE)并且似乎无法让它工作。当我手动使用设计中的“刷新”按钮(适用于任何指定的动词)时,下面的示例效果很好,但是当我取消注释“b_send_Click”中的部分时它不起作用。我正在寻找的是一个封装REST客户端的封装方法(在这个例子中'b_send_Click'的方式),然后在调用完成时采取一些动作(以'b_send_Click'中的注释部分为例)。有任何想法吗?顺便说一下,这可以作为异步REST调用的包装,但我无法使同步工作......

using Microsoft.Phone.Controls;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Windows;

namespace WP8Rest
{
    public partial class MainPage : PhoneApplicationPage
    {
        // global variables
        public static string url = "http://mywebsite.com/API/some_api";
        public static string request_body = "";
        public static HttpWebRequest client = null;
        public static HttpWebResponse response = null;
        public static string server_response = "";
        public static bool request_done = false;

        public MainPage()
        {
            InitializeComponent();
        }

        private void b_send_Click(object sender, RoutedEventArgs e)
        {
            rest_request(sender, e);

            /*
            while (!request_done)
            {
                Thread.Sleep(100);
            }

            if (response != null)
            {
                l_status_code.Text = response.StatusCode.ToString();
                l_status_description.Text = response.StatusDescription.ToString();
                l_response.Text = server_response;
            }
            else
            {
                l_status_code.Text = "0";
                l_status_description.Text = "Unable to complete request...";
                l_response.Text = "Unable to complete request...";
            }
             */
        }

        private void rest_request(object sender, RoutedEventArgs e)
        {
            request_done = false;
            server_response = "";
            request_body = tb_reqbody.Text;
            client = (HttpWebRequest)WebRequest.Create(url);

            client.Method = tb_verb.Text;
            client.AllowAutoRedirect = true;

            switch (tb_verb.Text)
            {
                case "GET":
                    client.BeginGetResponse(new AsyncCallback(GetResponseCallback), client);
                    break;

                case "PUT":
                    client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
                    client.ContentType = "application/json";
                    break;

                case "POST":
                    client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
                    client.ContentType = "application/json";
                    break;

                case "DELETE":
                    client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
                    client.ContentType = "application/json";
                    break;

                default:
                    MessageBox.Show("Use GET, PUT, POST, or DELETE.");
                    return;
            }

            l_response.Text = "Request sent...";
            return;
        }

        private static void GetRequestStreamCallback(IAsyncResult async_result)
        {
            HttpWebRequest request = (HttpWebRequest)async_result.AsyncState;
            Stream request_body_stream = request.EndGetRequestStream(async_result);
            byte[] request_body_bytearray = Encoding.UTF8.GetBytes(request_body);
            request_body_stream.Write(request_body_bytearray, 0, request_body.Length);
            request_body_stream.Close();
            request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
        }

        private static void GetResponseCallback(IAsyncResult async_result)
        {
            HttpWebRequest request = (HttpWebRequest)async_result.AsyncState;
            response = (HttpWebResponse)client.EndGetResponse(async_result);
            Stream response_body_stream = response.GetResponseStream();
            StreamReader stream_reader = new StreamReader(response_body_stream);
            server_response = stream_reader.ReadToEnd();            
            response_body_stream .Close();
            stream_reader.Close();
            response.Close();
            request_done = true;
        }

        private void b_refresh_Click(object sender, RoutedEventArgs e)
        {
            if (response != null)
            {
                l_response.Text = server_response;
                l_status_code.Text = response.StatusCode.ToString();
                l_status_description.Text = response.StatusDescription.ToString();
            }
            else
            {
                l_response.Text = "No response...";
            }
        }
    }
}

Per @ Industry86我能够安装Microsoft.Net.Http。将代码更改为:

private async void b_send_Click(object sender, RoutedEventArgs e)
{
    l_response.Text = myMethod();
}

async Task<string> myMethod()
{
    string address = "http://dev.getcube.com:65533/rest.svc/API/mirror";
    HttpClient client = new HttpClient();
    HttpResponseMessage response = await client.GetAsync(address);
    string responseText = await response.Content.ReadAsStringAsync();
    return responseText;
}

现在问题是它不会编译 - “无法将类型'System.Threading.Tasks.Task'隐式转换为'string'。我将b_send_Click中的行更改为l_response.Text = (myMethod()).Result;(不确定这是否是是否正确)当我点击“b_send”按钮时,它变为橙色,服务器永远不会看到请求。

2 个答案:

答案 0 :(得分:2)

创建新答案以专注于代码更改。

在异步方法调用前放置await

private async void b_send_Click(object sender, RoutedEventArgs e)
{
    l_response.Text = await myMethod();
}

当您使用.Result时,它会暂停该过程,直到结果返回,从而停止UI和其他所有内容。这是一个SO答案,详细说明了这个问题: https://stackoverflow.com/a/13703845/311393

快速课程:使用void返回值,异步方法将“即发即忘”并且永远不会期望返回。使用TaskTask<T>返回值时,调用的异步方法将暂停调用方法,直到完成为止。

因此b_send_Click将触发一个线程来做任何事情。当它使用适当的myMethod()关键字调用await(任务)时,它将以同步方式停止并等待它完成无论它正在做什么。

并且myMethod()有多个异步方法调用,但是那些也在等待它们,所以线程也会等待那些同步完成。

然后它返回到你的文本字段和UI,我假设在WP8中,异步地监听对它的文本字段的更改。

答案 1 :(得分:1)

就个人而言,我会使用Windows 8中存在的HTTPClient(它非常棒),如果尚未完全发布,目前可能仍然处于WP8(https://nuget.org/packages/Microsoft.Net.Http)的测试阶段。语法更小更简单。 或者使用RestSharp(http://restsharp.org/),但async / await的东西并不健全。 RestSharp非常擅长序列化。

说,你还需要了解异步操作是如何发生的。您正在调用异步操作,并在没有“等待”的情况下继续前进。因此:

l_response.Text = server_response;

将不会被设置,因为如果没有Thread.Sleep(100),代码将触发异步调用并继续前进,并且server_response在到达时仍然是 null 一部分。

如果你想等待该调用的返回,你需要使用“await”命令并在方法声明中包含异步指示符并返回一个Task(其中object可以是你想要的任何东西)返回)。使用HttpClient的示例:

async Task<string> myMethod()
{
    string address = "http://mywebsite.com/API/some_api";
    HttpClient client = new HttpClient();
    HttpResponseMessage response = await client.GetAsync(address);
    string responseText = await response.Content.ReadAsStringAsync();
    return responseText;
}

并且生成的字符串“responseText”将具有要解析的JSON内容。当然,如果你正在寻找一个流,它也在那里。

你还必须记住,这个方法本身需要来自UI线程的等待,并且声明将需要是异步的:

private async void b_send_Click(object sender, RoutedEventArgs e)
{
    someTextBox.Text = myMethod();
}

和事件处理程序通常应该是唯一返回 void 的异步方法。