91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在c#中設置HttpClient超時

發布時間:2021-03-18 15:17:51 來源:億速云 閱讀:2466 作者:Leah 欄目:開發技術

這篇文章將為大家詳細講解有關怎么在c#中設置HttpClient超時,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

問題

如果你經常用HttpClient去調用Restfull接口或傳送文件,你可能會對HttpClient這個類處理Request(請求)超時的方式感到惱火,因為存在這兩個問題:

  • timeout(超時)只能在HttpClient的class級別處理。也就是說,一旦設置好了,所有httpClient下的請求都會應用同樣的超時設置,這顯然不靈活,如果能夠為每個request請求分別指定一個超時時間,將非常方便。

  • 當請求超時時,拋出的異常很不好辨認。你認為請求超時時,httpclient會拋出TimeoutException?,不,其實它會拋出一個TaskCanceledException,而單看這個異常,你一時還無法分辨是取消導致的還是真正超時導致的。

幸運的是,得益于HttpClient的靈活設計,可以非常容易的彌補此缺陷。
因此,我們將針對這兩個問題做出解決方案。讓我們回顧一下我們想要的:

  • 可以為每個request請求單獨設置超時時間

  • 當超時發生時,catch的異常是TimeoutException而不是TaskCanceledException。

為每個request設置超時值

怎樣將超時時間值和Request請求關聯起來呢?HttpRequestMessage這個類有個Properties的屬性,它是一個字典(Dictionary)類型的屬性,我們可以放入我們任何自定義需要的內容到這個屬性中。我們將使用這個屬性存儲請求(request)的超時時間,為了便于實現此功能,我們給HttpRequestMessage創建一個擴展方法:

public static class HttpRequestExtensions
{
  private static string TimeoutPropertyKey = "RequestTimeout";

  public static void SetTimeout(
    this HttpRequestMessage request,
    TimeSpan? timeout)
  {
    if (request == null)
      throw new ArgumentNullException(nameof(request));

    request.Properties[TimeoutPropertyKey] = timeout;
  }

  public static TimeSpan? GetTimeout(this HttpRequestMessage request)
  {
    if (request == null)
      throw new ArgumentNullException(nameof(request));

    if (request.Properties.TryGetValue(
        TimeoutPropertyKey,
        out var value)
      && value is TimeSpan timeout)
      return timeout;
    return null;
  }
}

這是一段很普通的代碼,timout參數是可null的TimeSpan值,我們現在可以給請求設置超時值,但是目前還沒有實際使用到這段代碼。

Http Handler

HttpClient使用 管道體系( pipeline architecture) 結構:每個請求都通過一系列類型為HttpMessageHandler的Handler處理,并且以相反順序逐級返回響應。有了這種機制,我們可以非常方便的加入我們自己的Handler來具體處理超時問題。如果您想了解更多,本文將對此進行更詳細的說明。

我們的自己的超時Handler將繼承DelegatingHandler,DelegatingHandler是一種設計為鏈式調用其他Handler的類(簡單提一下:DelegatingHandler內部有個InnerHandler成員變量,我們可以在調用innerHandler.SendAsync()前后對request、CancellationToken和response做相應處理)。要實現我們的Handler,我們重寫SendAsync方法。最小的實現如下所示:

class TimeoutHandler : DelegatingHandler
{
  protected async override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request,
    CancellationToken cancellationToken)
  {
    return await base.SendAsync(request, cancellationToken);
  }
}

上述代碼并沒有任何用處,因為只是將實際處理丟給了base.SendAsync,目前還沒有對TimeoutHandler進行任何加工處理,我們將逐步對其加強擴充,以達到我們的目的。

給Request加上超時處理

首先,讓我們給TimeoutHandler添加一個TimeSpan類型的DefaultTimeout屬性,這個默認超時時間是給沒有特意設置超時時間的請求使用的:

public TimeSpan DefaultTimeout { get; set; } = TimeSpan.FromSeconds(100);

就像HttpClient.Timeout一樣,我們也設置默認超時時間為100秒。

為了實現我們的超時處理,我們需要從request中獲取超時時間(如果request中沒有設置,則應用DefaultTimeout的值)。接著,我們創建一個在指定時間(超時時間)后將會被取消的CancellationToken,并把這個CancellationToken傳入到鏈的下一個Handler。這樣之后,如果指定超時時間內沒有獲取到response響應,我們剛剛創建的CancellationToken就會被取消(cancel)。

我們創建一個CancellationTokenSource,這個類可以創建和控制CancellationToken。它將根據超時時間來創建:

private CancellationTokenSource GetCancellationTokenSource(
  HttpRequestMessage request,
  CancellationToken cancellationToken)
{
  var timeout = request.GetTimeout() ?? DefaultTimeout;
  if (timeout == Timeout.InfiniteTimeSpan)
  {
    // No need to create a CTS if there's no timeout
    //不需要創建CTS,因為不處理超時(下面會講到)
    return null;
  }
  else
  {
    var cts = CancellationTokenSource
      .CreateLinkedTokenSource(cancellationToken);
    cts.CancelAfter(timeout);
    return cts;
  }
}

這里主要關注兩個點:

  • 如果request超時值為Timeout.InfiniteTimeSpan,程序并不會創建CancellationTokenSource,它將不會被取消,因此節省了無用的分配。也就是說在這種情況下,我們將不會處理超時。

  • 以上相反,我們創建了一個在指定timeout后被自動取消的CancellationTokenSource(因為調用了CancelAfter)。請注意,這個CTS連接了傳入參數的cancellationToken,這個cancellationToken其實來自SendAsync方法的實參。這樣做之后,當真正的超時發生,或者參數的cancellationToken自身被取消,CTS都會被取消。如果想要獲取跟多CancellationToken的內容,請訪問這篇文章

最后,我們修改下SendAsync方法,應用剛剛創建的CancellationTokenSource。

protected async override Task<HttpResponseMessage> SendAsync(
  HttpRequestMessage request,
  CancellationToken cancellationToken)
{
  using (var cts = GetCancellationTokenSource(request, cancellationToken))
  {
    return await base.SendAsync(
      request,
      cts?.Token ?? cancellationToken);
  }
}

我們創建了CTS后,把CTS的token傳入到base.SendAsync中,注意,我們使用cts?.Token是因為GetCancellationTokenSource返回的cts可能為null,如果cts為null,則直接使用參數自己的cancellationToken,我們就不做任何超時處理。

通過這一步,我們有了自己的超時Handler,可以為每個請求指定不同的超時時間。但是,當超時發生時,我們仍然只能捕獲到TaskCanceledException異常,這個問題很容易修復它。

拋出正確的異常

我們需要捕獲TaskCanceledException(或者它的基類OperationCanceledException),然后檢測cancellationToken參數是否是被取消的:

  • 如果是,說明這個cancel是調用者自身導致的,對此直接將異常上拋不處理

  • 如果不是,這意味著是因為我們的超時導致的cancel,因此,我們將拋出一個TimeoutException

這是最終的SendAsync方法:

protected async override Task<HttpResponseMessage> SendAsync(
  HttpRequestMessage request,
  CancellationToken cancellationToken)
{
  using (var cts = GetCancellationTokenSource(request, cancellationToken))
  {
    try
    {
      return await base.SendAsync(
        request,
        cts?.Token ?? cancellationToken);
    }
    catch(OperationCanceledException)
      when (!cancellationToken.IsCancellationRequested)
    {
      throw new TimeoutException();
    }
  }
}

我們使用了一個exception filter,通過這種方式,我們只cactch我們符合我們情況需要的異常,然后做相應處理。

至此,我們的超時Handler已經完成了,接下來看看怎么使用它

使用Handler

當創建一個HttpClient時,可以指定一個自己的Handler作為管道(pipeline)的第一個Handler。如果沒有指定,默認使用的是HttpClientHandler,這個handler直接發送請求到網絡上。為了使用我們自己的TimeoutHandler,我們需要先創建它,然后將timeoutHandler指定為httpClient的handler。在timeoutHandler中,指定InnerHandler為我們自己創建的HttpClientHandler,這樣實際的網絡請求就委托到了HttpClientHandler中。

var handler = new TimeoutHandler
{
  InnerHandler = new HttpClientHandler()
};

using (var client = new HttpClient(handler))
{
  client.Timeout = Timeout.InfiniteTimeSpan;
  ...
}

通過將httpclient的timeout設置為InfiniteTimeSpan來禁用默認的超時設置,如果不這樣做,默認超時會干擾我們自己的超時

現在,我們嘗試發送一個設定了5秒超時的請求到需要很久才能響應的服務器

var request = new HttpRequestMessage(HttpMethod.Get, "http://foo/");
request.SetTimeout(TimeSpan.FromSeconds(5));
var response = await client.SendAsync(request);

如果服務器在5秒內響應數據,我們將會捕獲到一個TimeoutException,而不是TaskCanceledException,因此事情似乎按預期進行。

為了檢測cancellation是否正確運行,我們傳入一個在2秒(比超時實際小)后會被取消的CancellationToken:

var request = new HttpRequestMessage(HttpMethod.Get, "http://foo/");
request.SetTimeout(TimeSpan.FromSeconds(5));
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
var response = await client.SendAsync(request, cts.Token);

這時,我們可以捕獲到TaskCanceledException,這正是我們期望的。

關于怎么在c#中設置HttpClient超時就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

鱼台县| 化德县| 鲜城| 湖北省| 方城县| 余江县| 全州县| 常德市| 周至县| 平武县| 莱阳市| 白山市| 乃东县| 永州市| 新竹县| 大邑县| 晋州市| 怀化市| 哈密市| 崇阳县| 宣威市| 永年县| 白城市| 江津市| 银川市| 广饶县| 分宜县| 华容县| 扶沟县| 深圳市| 唐海县| 合水县| 泽普县| 咸宁市| 永嘉县| 泊头市| 安宁市| 济南市| 靖宇县| 德庆县| 嫩江县|