基于事件的异步 WebRequest






4.25/5 (8投票s)
一个示例项目,其中包含一个 BackgroundWebRequest 组件,该组件可用于 WinForms 项目中执行异步 WebRequests。该项目展示了如何使用 WebRequest 和 WebResponse 对象,以及如何实现 MSDN 上描述的基于事件的异步模式。
引言
此代码示例旨在用作处理 System.Net
命名空间中的 WebRequest
和 WebResponse
对象,以及如何在 MSDN 网站上定义的基于事件的异步模式的参考。
使用 WebRequest 和 WebResponse
使用 WebRequest
对象非常简单。以下代码片段将为 www.google.com 创建一个 WebRequest
并获取响应。
Dim request As WebRequest = WebRequest.Create("http://www.google.com")
Dim response As WebResponse = request.GetResponse()
处理 WebResponse
稍微有点棘手。首先,您必须从流中读取响应,然后,根据 WebResponse.ContentType
,您需要弄清楚如何将其转换为所需的输出。以下函数将接受一个 WebResponse
并将结果读入 String
、Image
或 Byte
数组。
Public Shared Function ProcessResponseStream( _
ByVal response As WebResponse, _
Optional ByVal defaultCharset As String = "utf-8") _
As Object
Dim st As IO.Stream
st = response.GetResponseStream()
Dim mem As New IO.MemoryStream()
Dim buffer(1024) As Byte
Dim read As Integer = 0
Try
' Read the response stream into memory
Do
read = st.Read(buffer, 0, buffer.Length)
mem.Write(buffer, 0, read)
Loop While read > 0
st.Close()
response.Close()
' Reset the memory position so we can read
' from the stream.
mem.Position = 0
' Parse the content type (ContentType is an
' internal class).
Dim contentType As ContentType
contentType = _
New ContentType(response.ContentType)
Select Case contentType.Type
Case "text"
' we should be able to read any text
' content into a string (assuming we
' have the correct encoding type).
Dim result(CInt(mem.Length)) As Byte
mem.Read(result, 0, CInt(mem.Length))
' We need to get the appropriate
' charset in order to decode the
' byte array as a string.
' This information can be sent
' in the Content-Type. If it isn't
' we need to use the default
' charset.
Dim charset As String
charset = contentType.GetValue( _
"charset", _
defaultCharset)
' We have the charset, now get the
' Encoding object and decode the
' content into a string.
Dim enc As Encoding
enc = Encoding.GetEncoding(charset)
Return enc.GetString(result)
Case "image"
' We should be able to read most image
' types directly into an image.
Return Image.FromStream(mem)
Case Else
' Let the caller figure out how to
' handle this content.
Dim result(CInt(mem.Length)) As Byte
mem.Read(result, 0, CInt(mem.Length))
Return result
End Select
Finally
mem.Close()
End Try
End Function
如果 Web 资源需要身份验证,您可以使用 WebRequest.Credentials
属性提供它。 只需将其设置为 System.Net.NetworkCredential
对象即可。 这允许您将用户的姓名、密码和域名发送到服务器,以提供用户对 Web 内容的访问权限。
如果您想在会话之间存储用户的凭据,您需要确保数据安全。如果您想了解更多相关信息,请研究 .NET 中的 DataProtection API。 您可以在我的博客文章 在 .NET 中保护数据中阅读更多相关信息。
基于事件的异步模式
这与 BackgroundWorker
用于提供异步处理,并在调用线程上引发事件的模式相同。这是一种非常有用的模式,并且 .NET 线程库使其易于实现。 MSDN 文档比我能想到的任何东西都要好,所以我鼓励您阅读它(我不会在这里讨论任何细则)。
这种模式的关键是 System.ComponentModel.AsyncOperation
类。 这个类允许您进行跨线程调用。 基本上,您在主线程上实例化该类,将其传递给一个新线程,然后调用 AsyncOperation.Post(AddressOf MyMethod, MyArg)
。 然后 AsyncOperation
将使用 Windows 消息传递在调用线程上调用您的方法(您需要查看文档以了解此方面的细则:))。 完成线程后,调用 AsyncOperation.PostOperationCompleted(AddressOf MyMethod, MyArg)
。
如果您有兴趣了解我是如何发现这种模式的,您可以阅读我的博客文章 .NET 线程的圣杯。
BackgroundWebRequest 组件
NetLib 项目(包含在此文章的下载中)使用基于事件的异步模式来提供用于下载 Web 内容的多线程解决方案。 它可以被放到 WinForm 上,像 BackgroundWorker
组件一样使用。 只需设置几个属性,处理几个事件,然后调用 BackgroundWebRequest.GetResponseAsync
方法。 此方法将返回一个充当请求键的对象。 所有特定于特定请求的事件和方法都使用此对象来标识请求(它只是一个 System.Object
)。
以下代码示例展示了示例项目如何使用请求键来处理 BackgroundWebRequest.GetRequestCompleted
事件(注意,SetStatus
方法只是将标签设置为请求的状态)。
Private mRequestKey As Object
Private Sub txtAddress_Validated( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles txtAddress.Validated
' make sure we cancel the last request
wrcBrowser.CancelRequest(mRequestKey)
If txtAddress.Text = "" Then
mRequestKey = Nothing
browser.DocumentText = ""
Else
' GetResponseAsyc returns an object that is
' used to identify the specific web request.
' The value must be stored in order to
' perform operations on it (such as cancelling)
mRequestKey = wrcBrowser.GetResponseAsync( _
txtAddress.Text)
End If
End Sub
Private Sub wrcBrowser_GetRequestCompleted( _
ByVal sender As System.Object, _
ByVal e As GetRequestCompletedEventArgs) _
Handles wrcBrowser.GetRequestCompleted
' we only care about the last request that we made
If e.RequestKey IsNot mRequestKey Then Return
' This method simply displays the status in the
' status bar.
SetStatus(e.Status, e.Error)
If e.Status = WebRequestStatus.Complete Then
' The request completed successfully, dump the
' html into the web browser.
browser.DocumentText = CStr(e.ResponseResults)
lblAddress.Text = e.Uri
End If
End Sub
关于示例代码的几个注意事项。 该代码没有经过任何严格的测试。 在这一点上,它只是一个示例应用程序。 有许多不同的错误情况,我目前没有处理,这些情况应该是生产代码的处理范围(一些遗漏是有意的,另一些则不是,你必须猜猜哪些是哪些 :))。 使用代码的风险由您自己承担,如果您发现任何重大问题,请告诉我。