.net 在KeepAlive模式下理解HttpWebRequest
我知道这个话题已经讨论过很多次了,但我需要理解如何以正确的方式编写代码 我在协议版本HTTP1.1中多次使用相同的HttpWebRequest(到相同的url).net 在KeepAlive模式下理解HttpWebRequest,.net,vb.net,httpwebrequest,.net,Vb.net,Httpwebrequest,我知道这个话题已经讨论过很多次了,但我需要理解如何以正确的方式编写代码 我在协议版本HTTP1.1中多次使用相同的HttpWebRequest(到相同的url) Method = "POST" KeepAlive = True 但每次我需要发送不同的请求,并得到不同的响应 (注意,下面的代码不正确并引发异常) 当我运行代码时,第一次得到正确的响应,但当我尝试重用HttpWebRequest时,会在以下行引发异常: httpWebReq.ContentLength = byt
Method = "POST"
KeepAlive = True
但每次我需要发送不同的请求,并得到不同的响应
(注意,下面的代码不正确并引发异常)
当我运行代码时,第一次得到正确的响应,但当我尝试重用HttpWebRequest时,会在以下行引发异常:
httpWebReq.ContentLength = byteData.Length
例外情况是,在开始写入后无法设置此属性
搜索时,我找到了此主题:其中解释了要重用HttpWebRequest,必须关闭流和WebResponse,我这样做了,释放了资源 在本主题中也解释了同样的事情:
但在另一个主题中:
一位成员说不可能重用HttpWebRequest。
我混淆了重用和创建新的,我需要理解它指的是什么:连接还是请求 我想当我执行此指令时:
Dim httpWebReq = CType(Net.WebRequest.Create("http://www.contoso.com/"), Net.HttpWebRequest)
Dim newStream As IO.Stream = httpWebReq.GetRequestStream()
我应该创建HttpWebRequest类的一个实例,但我应该建立与此指令的连接:
Dim httpWebReq = CType(Net.WebRequest.Create("http://www.contoso.com/"), Net.HttpWebRequest)
Dim newStream As IO.Stream = httpWebReq.GetRequestStream()
我说的对吗?我认为这是需要澄清的,因为这句话的措辞可能会被视为误导:
1个WebRequest=>1个WebResponse。一旦WebRequest被初始化,您就不能对其进行任何更改 这在原则上仍然有效,但初始化的术语可能会混淆。更好的定义是:
在发出请求并返回WebResponse之后,您不能更改WebRequest的任何参数,直到WebResponse关闭(释放)之后。
WebResponse
返回结果后,可以显式或隐式关闭它(在中使用
块),您可以请求另一个,根据需要修改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 <------------|
|
必须保留LoginParameters
对象,因为它引用了一个CookieContainer
,其中包含身份验证后收到的cookie。当初始化新的WebRequest时,这些cookie被传递到服务器,作为请求的凭据已通过身份验证的“证据”。请注意,这些cookie会在一段时间后过期(当发出新的WebRequest时,它们会“刷新”,除非会话有时间限制)。如果是这种情况,登录过程将自动重复。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
导入系统.Net
导入System.Net.Security
导入System.IO
导入系统。安全
导入System.Security.Cryptography
导入System.Security.Cryptography.X509证书
导入系统文本
作为LoginObject的公共登录参数
公共类LoginObject
公共属性LogInUrl作为字符串
公共属性ResponseUrl作为字符串
作为字典的公共属性凭据(字符串的,字符串的)
公共属性状态代码作为HttpStatusCode
作为新CookieContainer()的公共属性CookieJar
末级
公共异步函数HttpLogIn(LogInParameters作为LoginObject)作为(LoginObject的)任务
将httpRequest设置为HttpWebRequest
Dim状态代码为HttpStatusCode
将最大跳数设置为整数=20
“Windows 7(.Net 4.5.1+必需):
'ServicePointManager.SecurityProtocol=SecurityProtocolType.Tls12
“Windows 10(.Net 4.5.1+必需):
ServicePointManager.SecurityProtocol=SecurityProtocolType.SystemDefault
“如果需要或用于测试
'ServicePointManager.ServerCertificateValidationCallback=CertificateValidation的地址
httpRequest=WebRequest.CreateHttp(LogInParameters.LogInUrl)
尝试
HTTP_RequestHeaderInstant(httpRequest,String.Empty,LogInParameters.CookieJar)
使用httpResponse作为HttpWebResponse=CType(等待httpRequest.GetResponseAsync(),HttpWebResponse)
StatusCode=httpResponse.StatusCode
终端使用
如果StatusCode=HttpStatusCode.OK或LSE StatusCode=HttpStatusCode.NoContent,则
'POST参数是URL编码的,编码的字符串转换为UTF8字符的字节数组
Dim EncodedParameters As Byte()=HTTP_EncodePOSTParameters(LogInParameters.Credentials)
httpRequest=WebRequest.CreateHttp(LogInParameters.LogInUrl)
httpRequest.Method=WebRequestMethods.Http.Post
httpRequest.ContentType=“应用程序/x-www-form-urlencoded”
httpRequest.ContentLength=EncodedParameters.Length
HTTP_RequestHeaderInstant(httpRequest,String.Empty,LogInParameters.CookieJar)
使用stream作为stream=wait-httpRequest.GetRequestStreamAsync()
stream.Write(EncodedParameters,0,EncodedParameters.Length)
终端使用
将跳数调整为整数=0
Dim Referer As String=LogInParameters.LogInUrl
Dim LastHttpMethod为String=httpRequest.Method
做
'评估身份验证重定向或页面移动
使用httpResponse作为HttpWebResponse=CType(等待httpRequest.GetResponseAsync(),HttpWebResponse)
StatusCode=httpResponse.StatusCode
LogInParameters.ResponseUrl=URIFromResponseLocation(httpResponse.ToString())