Mar*_*llo 5 .net vb.net httpwebrequest
我知道这个话题已经讨论过很多次了,但我需要了解如何以正确的方式编写代码。
我在协议版本 HTTP 1.1 中多次使用相同的 HttpWebRequest(到相同的 url)。
Method = "POST"
KeepAlive = True
Run Code Online (Sandbox Code Playgroud)
但每次我需要发送不同的请求,并得到不同的响应。
(注意。下一个代码不正确并引发异常)
Private Sub SendHttpWebReq()
Dim httpWebReq = CType(Net.WebRequest.Create("http://www.contoso.com/"), Net.HttpWebRequest)
httpWebReq.Method = "POST"
httpWebReq.KeepAlive = True
httpWebReq.ContentType = "application/x-www-form-urlencoded"
Dim myRequestString As New List(Of String) From {"abc", "def"}
Dim ContentList As New List(Of String)
For a = 0 To 1
Dim inputData As String = MyRequestString(a)
Dim postData As String = "firstone" + ChrW(61) + inputData
Dim encoding As New System.Text.ASCIIEncoding()
Dim byteData As Byte() = encoding.GetBytes(postData)
httpWebReq.ContentLength = byteData.Length
Dim newStream As IO.Stream = httpWebReq.GetRequestStream()
newStream.Write(byteData, 0, byteData.Length)
newStream.Flush()
newStream.Dispose()
Dim Response As Net.WebResponse = httpWebReq.GetResponse()
Dim ResponseStream As Io.Stream = Response.GetResponseStream()
Dim Content = New Io.MemoryStream()
ResponseStream.CopyTo(Content)
Response.Close()
Response.Dispose()
ResponseStream.Flush()
ResponseStream.Dispose()
ContentList.Add(System.Text.Encoding.UTF8.GetString(Content.ToArray))
Content = Nothing
Next
End Sub
Run Code Online (Sandbox Code Playgroud)
当我运行代码时,我第一次得到了正确的响应,但是当我尝试重用HttpWebRequest 时,在这一行抛出了一个异常:
httpWebReq.ContentLength = byteData.Length
Run Code Online (Sandbox Code Playgroud)
异常是写入开始后无法设置此属性
搜索中,我发现了这个主题:
我可以重用 HttpWebRequest 吗?
在那里解释说要重用HttpWebRequest,必须关闭 Stream 和 WebResponse,我做到了,释放了资源。
同样在本主题中,它解释了同样的事情:
Reusing HttpWebRequest Object
但是在另一个主题中:
写入开始后无法设置此属性!在 C# WebRequest 对象上
一位成员说不可能重用 HttpWebRequest。
我在重用和创建一个新的之间感到困惑,我需要了解KeepAlive它指的是什么:连接还是请求?
我想当我执行这条指令时:
Dim httpWebReq = CType(Net.WebRequest.Create("http://www.contoso.com/"), Net.HttpWebRequest)
Run Code Online (Sandbox Code Playgroud)
我应该创建一个 HttpWebRequest 类的实例,但我应该用这个指令建立连接:
Dim newStream As IO.Stream = httpWebReq.GetRequestStream()
Run Code Online (Sandbox Code Playgroud)
我对么?
我认为这需要一些澄清,因为这种说法可能被认为具有误导性,其措辞方式如下:
1 个 Web 请求 => 1 个 Web 响应。一旦 WebRequest 初始化,您就无法更改其中的任何内容。
原则上这仍然有效,但初始化术语可能会令人困惑。更好的定义是:
在发出请求并返回 WebResponse 后,您无法更改WebRequest的任何参数,直到WebResponse关闭(处置)为止。
aWebResponse返回结果后,可以将其关闭 - 显式或隐式(在块中Using) - 并且您可以请求另一个结果,根据需要修改WebRequest参数(例如将方法从 POST 更改为 GET)。
此外,在请求新的 WebResponse 之前必须重新初始化 WebRequest。如果不这样做,它就会恢复到默认值。
我在下面发布的代码是一个经典上下文(Web 登录请求)的示例,必须WebRequest在同一过程中多次重新初始化,以接收不确定数量的 WebResponse,直到目标地址(或登陆页面)被确定。到达。
这或多或少是这样的模式:
--------------
(GET or POST) | WebRequest | (Method is POST)
|---------> | GET/(POST) | <-----------| <-------------- |
| -------------- | |
| | | |
-------------- --------------- ------------------ --------------
| New | | WebResponse |--> | LogIn Required |-->| LogIn |
| Location | --------------- ------------------ | Address |
| (Referer | | --------------
| Set) | |
-------------- (Set Cookies)
| |
| ---------------
| | LogIn |
Redirection <----| OK |---NO---|
--------------- |
| |
YES |
(Set Cookies) |
| Request
--------------- Denied
| Response | |
| URI | |
--------------- |
| |
EXIT <------------|
|
Run Code Online (Sandbox Code Playgroud)
请注意,发出 WebRequest 时,如果访问所请求的资源 URI 需要身份验证,服务器可能不会用StatusCode 302 (Found)进行应答,301 (Moved)或者303 (Redirected),它可能只是将 StatusCode 设置为200 (OK)。重定向是隐式的,因为设置了“Location”标头,或者如果是 WebForm 登录,则检索到的 Html 页面包含重定向。
无论如何,在检测到重定向后,必须遵循新的重定向位置到达目的地。重定向可能由一个或多个组成Hops,通常必须手动解决(以验证我们是否被发送到我们真正想去的地方)。
关于keep-alive标题。客户端和/或服务器设置标头,以
提示对方已建立的连接应保持打开状态,至少在一段时间内,因为链接到当前事务的其他资源将被交换的机会很高。这可以防止创建大量昂贵的连接。此设置在和requests
中很常见,并且是.keep-aliveHttpFtpHttp 1.1
超文本传输协议 (HTTP) 保活标头 (IETF)
与 HTTP/1.0 持久连接 (IETF) 的兼容性
需要记住的是,keep-alive标头指的是通过 WebRequest 建立的连接,而不是 WebRequest 本身。当创建连接并指定应保持打开状态时,后续请求应维护connection: keep-alive标头以符合协议。
在 .NET 中HttpWebRequest,将KeepAlive属性设置为False, 相当于设置connection: closeHeader。
然而,管理连接和进程被授予访问权限的连接池的逻辑是由 ServicePointManager 管理的,使用ServicePoint作为每个连接请求的引用。
因此,WebRequest 可能会指定它要求创建的 Connection 应保持打开状态(因为它需要重复使用更多次),但 Connection 背后的真正逻辑(即如何建立、维护和管理)位于其他地方。
在 HTTP 2.0 中(StackOverflow/StackExchange 使用此协议),由于这个原因,keep-alive 设置被完全忽略。连接逻辑在更高的、独立的级别上进行管理。在 HTTP 2.0 中,持久连接可用于服务多个并发请求。
(...) 当您每 3 秒、每 10 秒或每 60 秒调用 1 个新的 HttpWebRequest 时?当我用 True 或 False 发送这些请求时有什么区别?
您设置该KeepAlive属性以提示连接管理器建立的连接应保持打开状态,因为您知道它将被重新使用。然而,ServicePointManager如果使用的协议提供了此设置并且在控制连接池复合体的内部逻辑所施加的限制内,则远程服务器和远程服务器都将遵守该请求。
它应该在 中设置HTTP 1.0,它是 中的默认设置HTTP 1.1,在 中被忽略HTTP 2.0。
由于在建立连接之前您无法知道将使用哪个协议,因此通常将其设置为keep-alive,因为到所请求资源的路由中的某些设备(特别是代理)可能需要显式设置(请阅读 IETF 文档)关于代理及其行为)。
在此示例中,WebRequest 在循环中重复初始化,并且每次都会处理底层 WebResponse,直到200 (OK)收到 StatusCode 或请求被拒绝或我们已重定向太多次(取消令牌在这里也可能很有用) 。
在示例中,main 方法应该这样调用:
Public Async Sub SomeMethodAsync()
LoginParameters = New LoginObject() With {
.CookieJar = New CookieContainer,
.LogInUrl = "[Some IP Address]",
.Credentials = New Dictionary(Of String, String)
}
LoginParameters.Credentials.Add("UserName", "[Username]")
LoginParameters.Credentials.Add("Email", "[email]")
LoginParameters.Credentials.Add("Password", "[Password]")
LoginParameters = Await HttpLogIn(LoginParameters)
7End Sub
Run Code Online (Sandbox Code Playgroud)
该LoginParameters对象必须保留,因为引用了 a CookieContainer,其中包含身份验证后收到的 Cookie。当初始化新的 WebRequest 时,这些 Cookie 会传递到服务器,作为请求的凭据已通过身份验证的“证据”。请注意,这些 Cookie 会在一段时间后过期(当发出新的 WebRequest 时,这些 Cookie 会“刷新”,除非会话有时间限制)。如果是这种情况,则会自动重复登录过程。
Imports System.Net
Imports System.Net.Security
Imports System.IO
Imports System.Security
Imports System.Security.Cryptography
Imports System.Security.Cryptography.X509Certificates
Imports System.Text
Public LoginParameters As LoginObject
Public Class LoginObject
Public Property LogInUrl As String
Public Property ResponseUrl As String
Public Property Credentials As Dictionary(Of String, String)
Public Property StatusCode As HttpStatusCode
Public Property CookieJar As New CookieContainer()
End Class
Public Async Function HttpLogIn(LogInParameters As LoginObject) As Task(Of LoginObject)
Dim httpRequest As HttpWebRequest
Dim StatusCode As HttpStatusCode
Dim MaxHops As Integer = 20
' Windows 7 (.Net 4.5.1+ required):
'ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
' Windows 10 (.Net 4.5.1+ required):
ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault
'If needed or for testing
'ServicePointManager.ServerCertificateValidationCallback = AddressOf CertificateValidation
httpRequest = WebRequest.CreateHttp(LogInParameters.LogInUrl)
Try
HTTP_RequestHeadersInit(httpRequest, String.Empty, LogInParameters.CookieJar)
Using httpResponse As HttpWebResponse = CType(Await httpRequest.GetResponseAsync(), HttpWebResponse)
StatusCode = httpResponse.StatusCode
End Using
If StatusCode = HttpStatusCode.OK OrElse StatusCode = HttpStatusCode.NoContent Then
'POST Parameters are URLEncoded and the encoded strings converted to a Byte array of UTF8 chars
Dim EncodedParameters As Byte() = HTTP_EncodePOSTParameters(LogInParameters.Credentials)
httpRequest = WebRequest.CreateHttp(LogInParameters.LogInUrl)
httpRequest.Method = WebRequestMethods.Http.Post
httpRequest.ContentType = "application/x-www-form-urlencoded"
httpRequest.ContentLength = EncodedParameters.Length
HTTP_RequestHeadersInit(httpRequest, String.Empty, LogInParameters.CookieJar)
Using stream As Stream = Await httpRequest.GetRequestStreamAsync()
stream.Write(EncodedParameters, 0, EncodedParameters.Length)
End Using
Dim Hops As Integer = 0
Dim Referer As String = LogInParameters.LogInUrl
Dim LastHttpMethod As String = httpRequest.Method
Do
'Evaluate Authentication redirect or page moved
Using httpResponse As HttpWebResponse = CType(Await httpRequest.GetResponseAsync(), HttpWebResponse)
StatusCode = httpResponse.StatusCode
LogInParameters.ResponseUrl = URIFromResponseLocation(httpResponse).ToString()
End Using
If (StatusCode = HttpStatusCode.Moved) OrElse
(StatusCode = HttpStatusCode.Found) OrElse
(StatusCode = HttpStatusCode.RedirectMethod) OrElse
(StatusCode = HttpStatusCode.RedirectKeepVerb) Then
httpRequest = WebRequest.CreateHttp(LogInParameters.ResponseUrl)
HTTP_RequestHeadersInit(httpRequest, Referer, LogInParameters.CookieJar)
If StatusCode = HttpStatusCode.RedirectKeepVerb Then
httpRequest.Method = LastHttpMethod
Else
LastHttpMethod = httpRequest.Method
End If
End If
If (CType(StatusCode, Integer) > 320) OrElse Hops >= MaxHops Then
Exit Do
End If
Hops += 1
Loop While (StatusCode <> HttpStatusCode.OK)
If StatusCode = HttpStatusCode.OK Then
LogInParameters.CookieJar = httpRequest.CookieContainer
End If
End If
Catch exW As WebException
StatusCode = If(exW.Response IsNot Nothing,
CType(exW.Response, HttpWebResponse).StatusCode,
CType(exW.Status, HttpStatusCode))
Catch exS As System.Exception
StatusCode = CType(WebExceptionStatus.RequestCanceled, HttpStatusCode)
Finally
ServicePointManager.ServerCertificateValidationCallback = Nothing
End Try
LogInParameters.StatusCode = StatusCode
Return LogInParameters
End Function
Private Sub HTTP_RequestHeadersInit(ByRef httpReq As HttpWebRequest, Referer As String, CookiesJar As CookieContainer)
httpReq.Date = DateTime.Now
httpReq.CookieContainer = CookiesJar
httpReq.KeepAlive = True
httpReq.ConnectionGroupName = Guid.NewGuid().ToString()
httpReq.AllowAutoRedirect = False
httpReq.AutomaticDecompression = DecompressionMethods.GZip Or DecompressionMethods.Deflate
httpReq.ServicePoint.Expect100Continue = False
httpReq.Referer = Referer
httpReq.UserAgent = "Mozilla/5.0 (Windows NT 10; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0"
httpReq.Accept = "ext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
httpReq.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-US;q=0.9,en;q=0.5")
httpReq.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate;q=0.8")
httpReq.Headers.Add(HttpRequestHeader.CacheControl, "no-cache")
End Sub
Private Function HTTP_EncodePOSTParameters(PostParameters As Dictionary(Of String, String)) As Byte()
Dim Encoder As New System.Text.UTF8Encoding()
Dim CredentialValues As New StringBuilder()
Dim _first As Boolean = True
For Each CurrentKeyPair As KeyValuePair(Of String, String) In PostParameters
If _first = False Then CredentialValues.Append("&")
CredentialValues.AppendFormat("{0}={1}", WebUtility.UrlEncode(CurrentKeyPair.Key),
WebUtility.UrlEncode(CurrentKeyPair.Value))
_first = False
Next
Return Encoder.GetBytes(CredentialValues.ToString())
End Function
Private Function URIFromResponseLocation(Response As HttpWebResponse) As System.Uri
Dim uri As Uri
Dim Location As String = Response.Headers("Location")
Try
If uri.IsWellFormedUriString(Location, UriKind.Absolute) Then
uri = New Uri(Location, UriKind.Absolute)
Else
Dim HostUri As String = Response.ResponseUri.GetComponents(UriComponents.SchemeAndServer,
UriFormat.Unescaped) + Location
uri = If(uri.IsWellFormedUriString(HostUri, UriKind.Absolute),
New Uri(HostUri),
New Uri(Response.ResponseUri.GetComponents(UriComponents.Scheme, UriFormat.Unescaped) +
Response.ResponseUri.Host + Location))
End If
Catch ExceptionOnInvalidUri As Exception
uri = New Uri(Location, UriKind.Relative)
End Try
Return uri
End Function
Private Function CertificateValidation(sender As Object,
CACert As X509Certificate,
CAChain As X509Chain,
PolicyErrors As SslPolicyErrors) As Boolean
'This method, as it is, accepts a Server certificate in any case
'It could be eventually adapted to refuse a connection (returning false)
'if the certificate is invalid, expired or from a untrusted path
If (PolicyErrors = SslPolicyErrors.None) Then Return True
'If a Certificated must be added to the Chain, uncomment the code below,
'selecting a Certificate in the Local (or other) Storage
'Dim MyCert As X509Certificate2 = New X509Certificate2("[localstorage]/[ca.cert]")
'CAChain.ChainPolicy.ExtraStore.Add(MyCert)
'CAChain.Build(MyCert)
'For Each CACStatus As X509ChainStatus In CAChain.ChainStatus
' If (CACStatus.Status <> X509ChainStatusFlags.NoError) And
' (CACStatus.Status <> X509ChainStatusFlags.UntrustedRoot) Then
' Return False
' End If
'Next
Return True
End Function
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4590 次 |
| 最近记录: |