-2

我已经查看了类似问题的一些答案,但似乎找不到适用于我正在做的事情的东西。我需要使用 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...";
            }
        }
    }
}

根据@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' 按钮,它变成橙色,服务器永远不会看到请求。

4

2 回答 2

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 返回值,异步方法将“一劳永逸”并且永远不会期望返回。使用TaskorTask<T>返回值,调用的异步方法将暂停调用方法,直到它完成。

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

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

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

于 2013-04-29T22:31:56.240 回答
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在到达该部分时仍将为空。

如果您想等待该调用的返回,您需要使用“await”命令并在方法声明中包含 async 指示符并返回一个 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的异步方法。

于 2013-04-29T20:17:46.777 回答