【问题标题】:Why is my HttpClient.SendAsync being cancelled?为什么我的 HttpClient.SendAsync 被取消了?
【发布时间】:2014-09-12 15:14:43
【问题描述】:

我正在使用我为 HttpClient 制作的包装类。当我调用 SendAsync 时,它会抛出一个异常,指出“任务已取消”。

我不明白为什么会发生这种情况,我没有将取消令牌传递给它,我正在使用 Await 在返回之前等待结果。有人可以帮忙吗?

编辑 1:我读到如果编码不正确,HttpClient 和 Using 在 .NET 4.0 中存在问题。 http://forums.asp.net/t/1863442.aspx?HttpClient+Error+A+Task+was+canceled+ 。但是,我使用的是 .NET 4.5 和 Await 关键字,所以我不应该遇到这个问题。此外,当我在抛出异常的断点期间将鼠标悬停在 HttpClient 对象上时,它并不表示该对象已被释放。

发生异常的 HttpClientWrapper 类中的代码 sn-p;

Try
  Return Await Client.SendAsync(Message, HttpCompletionOption.ResponseHeadersRead)
Catch ex As Exception
  If DebugMode Then Output(OutputBufferKey, Method.ToString & " " & Url & " " & ex.Message, OutputType.Error)
End Try

自定义 HttpClientWrapper 类;

Public Class HttpClientWrapper
  Implements IDisposable

  Protected Handler As New HttpClientHandler
  Protected Client As HttpClient = Nothing

  Public Property OutputBufferKey As String = Nothing
  Public Property CancellationToken As CancellationToken? = Nothing

  Public Sub New(Optional Proxy As WebProxy = Nothing, Optional Cookies As CookieContainer = Nothing, Optional UserAgent As String = Nothing, _
                 Optional OutputBufferKey As String = Nothing, Optional CancellationToken As CancellationToken? = Nothing)
    Me.OutputBufferKey = OutputBufferKey
    Me.CancellationToken = CancellationToken

    If UserAgent Is Nothing Then UserAgent = RandomString(UserAgents)

    SetCookies(Cookies)
    SetProxy(Proxy)

    Handler.AutomaticDecompression = DecompressionMethods.GZip Or DecompressionMethods.Deflate
    Client = New HttpClient(Handler, True)

    Client.Timeout = New TimeSpan(DefaultTimeout)
    Client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate")
    Client.DefaultRequestHeaders.Add("Accept-Language", "en-US,en;q=0.5")
    Client.DefaultRequestHeaders.Add("DNT", "1")
    Client.DefaultRequestHeaders.Add("User-Agent", UserAgent)
  End Sub

  Public Sub New(User As User, Optional OutputBufferKey As String = Nothing, Optional CancellationToken As CancellationToken? = Nothing)
    Me.New(User.GetProxy, User.Cookies, User.UserAgent, OutputBufferKey, CancellationToken)
  End Sub

  Public Sub New(User As User, Task As Task)
    Me.New(User.GetProxy, User.Cookies, User.UserAgent, Task.OutputBufferKey, Task.CancellationToken.Token)
  End Sub

  Public Sub SetTimeout(Value As Integer)
    Client.Timeout = New TimeSpan(Value)
  End Sub

  Private Sub SetCookies(Cookies As CookieContainer)
    If Cookies Is Nothing Then
      Handler.UseCookies = False
    Else
      Handler.UseCookies = True
      Handler.CookieContainer = Cookies
    End If
  End Sub

  Private Sub SetProxy(Proxy As WebProxy)
    If Proxy Is Nothing Then
      Handler.UseProxy = False
    Else
      Handler.UseProxy = True
      Handler.Proxy = Proxy
    End If
  End Sub

  Public Async Function WebRequestByteAsync(Url As String, Optional RequestMethod As RequestMethod = RequestMethod.GET, Optional Content As Object = Nothing, _
                                              Optional ContentType As ContentType = ContentType.Default, Optional Accept As String = DefaultAcceptString, _
                                              Optional AdditionalHeaders As NameValueCollection = Nothing, Optional Referer As String = Nothing, Optional NoCache As Boolean = False) _
                                   As Tasks.Task(Of Byte())
    Using Response As HttpResponseMessage = Await WebRequestAsync(Url, RequestMethod, Content, ContentType, Accept, AdditionalHeaders, Referer, NoCache)
      If Response IsNot Nothing Then
        If Response.StatusCode = HttpStatusCode.OK Then
          Return Await GetResponseByteAsync(Response)
        Else
          Output(OutputBufferKey, Url & " did not return OK (200)", OutputType.Error)
        End If
      End If
      Return Nothing
    End Using
  End Function

  Public Async Function WebRequestStringAsync(Url As String, Optional RequestMethod As RequestMethod = RequestMethod.GET, Optional Content As Object = Nothing, _
                                              Optional ContentType As ContentType = ContentType.Default, Optional Accept As String = DefaultAcceptString, _
                                              Optional AdditionalHeaders As NameValueCollection = Nothing, Optional Referer As String = Nothing, Optional NoCache As Boolean = False) _
                                   As Tasks.Task(Of String)
    Using Response As HttpResponseMessage = Await WebRequestAsync(Url, RequestMethod, Content, ContentType, Accept, AdditionalHeaders, Referer, NoCache)
      If Response IsNot Nothing Then
        If Response.StatusCode = HttpStatusCode.OK Then
          Return Await GetResponseStringAsync(Response)
        Else
          Output(OutputBufferKey, Url & " did not return OK (200)", OutputType.Error)
        End If
      End If
      Return Nothing
    End Using
  End Function

  Public Async Function WebRequestAsync(Url As String, Optional RequestMethod As RequestMethod = RequestMethod.GET, Optional Content As Object = Nothing, _
                                              Optional ContentType As ContentType = ContentType.Default, Optional Accept As String = DefaultAcceptString, _
                                              Optional AdditionalHeaders As NameValueCollection = Nothing, Optional Referer As String = Nothing, Optional NoCache As Boolean = False, _
                                              Optional CanBeCancelled As Boolean = True) _
                                   As Tasks.Task(Of HttpResponseMessage)

    Dim Method As HttpMethod = Nothing
    Select Case RequestMethod
      Case Variables.RequestMethod.DELETE : Method = HttpMethod.Delete
      Case Variables.RequestMethod.GET : Method = HttpMethod.Get
      Case Variables.RequestMethod.OPTIONS : Method = HttpMethod.Options
      Case Variables.RequestMethod.POST : Method = HttpMethod.Post
      Case Variables.RequestMethod.PUT : Method = HttpMethod.Put
    End Select

    'prepare message
    Dim Message As New HttpRequestMessage(Method, Url)
    Message.Headers.ExpectContinue = False
    Message.Headers.TryAddWithoutValidation("Accept", Accept)
    If Referer IsNot Nothing Then Message.Headers.Add("Referer", Referer)
    If NoCache Then
      Message.Headers.Add("Pragma", "no-cache")
      Message.Headers.Add("Cache-Control", "no-cache")
    End If
    If AdditionalHeaders IsNot Nothing Then
      For Each Key In AdditionalHeaders.AllKeys
        Message.Headers.TryAddWithoutValidation(Key, AdditionalHeaders(Key))
      Next
    End If

    'set content
    If Content IsNot Nothing Then
      Dim ContentTypeString As String = GetEnumDescription(ContentType)

      Dim ContentBytes As Byte() = Nothing
      If TypeOf Content Is String Then
        ContentBytes = Encoding.UTF8.GetBytes(CType(Content, String))
      ElseIf TypeOf Content Is Byte() Then
        ContentBytes = CType(Content, Byte())
      ElseIf TypeOf Content Is MultiPartPostData Then
        Dim MultiPartPostData As MultiPartPostData = CType(Content, MultiPartPostData)
        ContentBytes = MultiPartPostData.Bytes
        ContentTypeString += "; boundary=" & MultiPartPostData.Boundary
      End If

      Dim ByteArrayContent As New ByteArrayContent(ContentBytes)
      ByteArrayContent.Headers.Add("Content-Type", ContentTypeString)
      Message.Content = ByteArrayContent
    End If

    'get response
    Output(OutputBufferKey, RequestMethod.ToString & " " & Url, OutputType.Debug)

    Try
      Return Await Client.SendAsync(Message, HttpCompletionOption.ResponseHeadersRead)
    Catch ex As Exception
      If DebugMode Then Output(OutputBufferKey, Method.ToString & " " & Url & " " & ex.Message, OutputType.Error)
    End Try

    Return Nothing
  End Function

  Public Async Function GetResponseByteAsync(Response As HttpResponseMessage) As Tasks.Task(Of Byte())
    If Response Is Nothing Then
      If DebugMode Then Output(OutputBufferKey, "Unable read content from response message: Is nothing", OutputType.Error)
    Else
      Try
        Return Await Response.Content.ReadAsByteArrayAsync
      Catch ex As Exception
        If DebugMode Then Output(OutputBufferKey, "Unable read content from " & Response.RequestMessage.RequestUri.ToString & ": " & ex.Message, OutputType.Error)
      End Try
    End If

    Return Nothing
  End Function

  Public Async Function GetResponseStringAsync(Response As HttpResponseMessage) As Tasks.Task(Of String)
    If Response Is Nothing Then
      If DebugMode Then Output(OutputBufferKey, "Unable read content from response message: Is nothing", OutputType.Error)
    Else
      Try
        Return Await Response.Content.ReadAsStringAsync
      Catch ex As Exception
        If DebugMode Then Output(OutputBufferKey, "Unable read content from " & Response.RequestMessage.RequestUri.ToString & ": " & ex.Message, OutputType.Error)
      End Try
    End If

    Return Nothing
  End Function

#Region "IDisposable Support"
  Private disposedValue As Boolean ' To detect redundant calls

  ' IDisposable
  Protected Overridable Sub Dispose(disposing As Boolean)
    If Not Me.disposedValue Then
      If disposing Then
        If Client IsNot Nothing Then Client.Dispose()
      End If

    End If
    Me.disposedValue = True
  End Sub

  ' This code added by Visual Basic to correctly implement the disposable pattern.
  Public Sub Dispose() Implements IDisposable.Dispose
    ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
    Dispose(True)
    GC.SuppressFinalize(Me)
  End Sub
#End Region

End Class

主要代码;

Module Main

  Dim ProxyUrl As String = "proxies.txt"

  Sub main()
    DebugMode = True
    OutputToConsole = True

    Dim Proxies As New ProxyList(ProxyUrl)

    Try
      Run(Proxies).GetAwaiter.GetResult()
    Catch ex As Exception
      MsgBox(ex.Message)
    End Try

    Console.ReadLine()

  End Sub

  Public Async Function Run(Proxies As ProxyList) As Tasks.Task

    Dim Proxy As WebProxy = Await Proxies.GetProxy

    Using Client As New HttpClientWrapper(Proxy)

      Dim Response As String = Await Client.WebRequestStringAsync("http://www.google.com")
      If Response IsNot Nothing Then Console.WriteLine(Response)

    End Using

  End Function

End Module

【问题讨论】:

    标签: .net vb.net task-parallel-library dotnet-httpclient


    【解决方案1】:

    HTTPClient 说在超时时任务已被取消:你确定你有足够大的超时/网络服务及时响应吗?

    【讨论】:

    • 感谢您为我指出正确的方向,我正在使用新的 TimeSpan(Ticks) 设置超时,滴答数为 8000。我将滴答声误认为是毫秒。问题解决了。
    • 酷!就像未来的一个好主意 - 如果您将异常转储到问题中,那将是一个更好的问题。
    • 欣赏建议,但不确定您的意思。是否有一种简单的方法可以获取异常的所有详细信息以放入问题中?
    • 只需将 except.ToString() 输出复制到问题中并将其格式化为代码/引用
    【解决方案2】:

    我遇到了同样的问题。

    我是这样配置 HttpClientFactory 的:

    services.AddHttpClient(string.Empty, c =>
                {
                   c.Timeout = TimeSpan.FromSeconds(15);
                })
            .ConfigurePrimaryHttpMessageHandler((c) => new HttpClientHandler
                {                                                                   
                    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
                });
    

    然后调用

    var response = await client.SendAsync(request);
    

    原来将自动解压改为 GZip 解决了这个问题:

    AutomaticDecompression = DecompressionMethods.GZip
    

    真的很奇怪。

    【讨论】:

      猜你喜欢
      • 2018-03-31
      • 2013-02-17
      • 1970-01-01
      • 2013-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-30
      • 2016-06-20
      相关资源
      最近更新 更多