获得异步HttpWebRequest的响应

21

我想知道是否有一种简单的方法来获取异步httpwebrequest的响应。

我已经看到了这个问题here,但我只是想以字符串的形式返回响应(通常为json或xml)到另一个方法中,我可以在那里对它进行解析和处理。

这里是一些代码:

我这里有两个静态方法,我认为它们是线程安全的,因为所有参数都被传递进去了,并且没有共享的局部变量被方法使用?

public static void MakeAsyncRequest(string url, string contentType)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.ContentType = contentType;
    request.Method = WebRequestMethods.Http.Get;
    request.Timeout = 20000;
    request.Proxy = null;

    request.BeginGetResponse(new AsyncCallback(ReadCallback), request);
}

private static void ReadCallback(IAsyncResult asyncResult)
{
    HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
    try
    {
        using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult))
        {
            Stream responseStream = response.GetResponseStream();
            using (StreamReader sr = new StreamReader(responseStream))
            {
                //Need to return this response 
                string strContent = sr.ReadToEnd();
            }
       }
       manualResetEvent.Set();
    }
    catch (Exception ex)
    {
        throw ex;
   }
}

你发布的代码在我移除多余的manualResetEvent.Set()后正常运行 - 你遇到了什么问题? - James Manning
@JamesManning 你好,那是个笔误,我想找一个更简单的方法来获取结果。你提供的(Task<T>)正好符合我的要求。我刚刚从同步请求转换过来,似乎有更多的事情要做。谢谢。 - gdp
4个回答

44
假设问题是您无法轻松获取返回的内容,则最简单的方法可能是使用异步/等待,如果可以使用它。如果您使用的是.NET 4.5,则更好的选择是切换到HttpClient,因为它是“本地”异步的。

在使用.NET 4和C# 4时,仍然可以使用任务(Task)来包装这些内容,并使访问最终结果变得更加容易。例如,一种选项如下所示。请注意,它将Main方法阻塞直到内容字符串可用,但在“真实”的情况下,您很可能会将该任务传递给其他内容或另一个ContinueWith,或者采取其他操作。

void Main()
{
    var task = MakeAsyncRequest("http://www.google.com", "text/html");
    Console.WriteLine ("Got response of {0}", task.Result);
}

// Define other methods and classes here
public static Task<string> MakeAsyncRequest(string url, string contentType)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.ContentType = contentType;
    request.Method = WebRequestMethods.Http.Get;
    request.Timeout = 20000;
    request.Proxy = null;

    Task<WebResponse> task = Task.Factory.FromAsync(
        request.BeginGetResponse,
        asyncResult => request.EndGetResponse(asyncResult),
        (object)null);

    return task.ContinueWith(t => ReadStreamFromResponse(t.Result));
}

private static string ReadStreamFromResponse(WebResponse response)
{
    using (Stream responseStream = response.GetResponseStream())
    using (StreamReader sr = new StreamReader(responseStream))
    {
        //Need to return this response 
        string strContent = sr.ReadToEnd();
        return strContent;
    }
}

一个快速的问题,当访问MakeAsyncRequest()方法的Result属性时,我如何确保结果在我尝试使用响应时总是已经完成。 - gdp
如果Result属性尚未完成,则会阻塞它,直到完成为止。 - James Manning
好的,那么请求将异步发生,只有流的读取会被阻塞。 - gdp
@geepie - 正确的,无论哪个线程访问Result属性(在其准备好之前),都会被阻塞,直到任务完成(并返回结果)或失败(并抛出异常)。 - James Manning
请求中的 request.Timeout = 20000; 在异步获取请求中是无用的。 - xiaoyifang
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Ashish-BeJovial

8

"如果你使用的是 .Net 4.5,最好切换到 HttpClient,因为它是“原生”的 async。" - James Manning 给出了绝对正确的答案。

这个问题是大约两年前提出的。现在我们有了 .Net Framework 4.5,它提供了强大的异步方法。使用 HttpClient。考虑以下代码:

 async Task<string> HttpGetAsync(string URI)
    {
        try
        {
            HttpClient hc = new HttpClient();
            Task<Stream> result = hc.GetStreamAsync(URI);

            Stream vs = await result;
            StreamReader am = new StreamReader(vs);

            return await am.ReadToEndAsync();
        }
        catch (WebException ex)
        {
            switch (ex.Status)
            {
                case WebExceptionStatus.NameResolutionFailure:
                    MessageBox.Show("domain_not_found", "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                break;
                    //Catch other exceptions here
            }
        }
    }

要使用 HttpGetAsync(),需要创建一个新的方法,并让它也是“async”的。由于我们需要在 GetWebPage() 方法中使用 “await”,因此必须使用“async”。
async void GetWebPage(string URI)
        {
            string html = await HttpGetAsync(URI);
            //Do other operations with html code
        }

现在,如果你想异步获取网页HTML源代码,只需调用GetWebPage("web-address...")。即使Stream读取也是异步的。
注意:要使用HttpClient,需要 .Net Framework 4.5。此外,您需要在项目中添加System.Net.Http引用,并添加"using System.Net.Http"以便轻松访问。
有关此方法如何工作的更多信息,请访问:http://msdn.microsoft.com/en-us/library/hh191443(v=vs.110).aspx 使用异步操作:Async in 4.5: Worth the Await

2
一旦你开始使用异步,就再也回不去了。从那里起,你只能访问异步的回调函数。你可以增加复杂性并进行一些线程和等待处理,但这可能会非常痛苦。
从技术上讲,当你需要等待结果时,你也可以使线程休眠,但我不建议这样做,此时最好直接进行普通的http请求。
在C# 5中,它们有async/await命令,这将使得将异步调用的结果传递到主线程变得更容易。

线程是我需要理解的内容。感谢您的输入。 - gdp

0
public static async Task<byte[]> GetBytesAsync(string url) {
    var request = (HttpWebRequest)WebRequest.Create(url);
    using (var response = await request.GetResponseAsync())
    using (var content = new MemoryStream())
    using (var responseStream = response.GetResponseStream()) {
        await responseStream.CopyToAsync(content);
        return content.ToArray();
    }
}

public static async Task<string> GetStringAsync(string url) {
    var bytes = await GetBytesAsync(url);
    return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接