欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

c# HttpClient設(shè)置超時(shí)的步驟

 更新時(shí)間:2021年03月18日 08:37:39   作者:陳開華  
這篇文章主要介紹了c# HttpClient設(shè)置超時(shí)的步驟,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下

HttpClient作為官方推薦的http客戶端,相比之前的WebClient和WebRequest好用了很多,但默認(rèn)無法為每個(gè)請(qǐng)求單獨(dú)設(shè)置超時(shí),只能給HttpClient設(shè)置默認(rèn)超時(shí),使用起來不太方便。

聲明:本文主要是翻譯自THOMAS LEVESQUE'S .NET BLOG的文章:Better timeout handling with HttpClient。
由于工作原因,需要用c#,就語法層而言,c#確實(shí)比java優(yōu)秀,一些庫接口封裝也更方便簡(jiǎn)潔。特別是HttpClient,結(jié)合了task異步模型,使用起來非常順手。
本人水平有限,如有問題,還望各位多多海涵,不吝賜教

問題

如果你經(jīng)常用HttpClient去調(diào)用Restfull接口或傳送文件,你可能會(huì)對(duì)HttpClient這個(gè)類處理Request(請(qǐng)求)超時(shí)的方式感到惱火,因?yàn)榇嬖谶@兩個(gè)問題:

  • timeout(超時(shí))只能在HttpClient的class級(jí)別處理。也就是說,一旦設(shè)置好了,所有httpClient下的請(qǐng)求都會(huì)應(yīng)用同樣的超時(shí)設(shè)置,這顯然不靈活,如果能夠?yàn)槊總€(gè)request請(qǐng)求分別指定一個(gè)超時(shí)時(shí)間,將非常方便。
  • 當(dāng)請(qǐng)求超時(shí)時(shí),拋出的異常很不好辨認(rèn)。你認(rèn)為請(qǐng)求超時(shí)時(shí),httpclient會(huì)拋出TimeoutException?,不,其實(shí)它會(huì)拋出一個(gè)TaskCanceledException,而單看這個(gè)異常,你一時(shí)還無法分辨是取消導(dǎo)致的還是真正超時(shí)導(dǎo)致的。

幸運(yùn)的是,得益于HttpClient的靈活設(shè)計(jì),可以非常容易的彌補(bǔ)此缺陷。
因此,我們將針對(duì)這兩個(gè)問題做出解決方案。讓我們回顧一下我們想要的:

  • 可以為每個(gè)request請(qǐng)求單獨(dú)設(shè)置超時(shí)時(shí)間
  • 當(dāng)超時(shí)發(fā)生時(shí),catch的異常是TimeoutException而不是TaskCanceledException。

為每個(gè)request設(shè)置超時(shí)值

怎樣將超時(shí)時(shí)間值和Request請(qǐng)求關(guān)聯(lián)起來呢?HttpRequestMessage這個(gè)類有個(gè)Properties的屬性,它是一個(gè)字典(Dictionary)類型的屬性,我們可以放入我們?nèi)魏巫远x需要的內(nèi)容到這個(gè)屬性中。我們將使用這個(gè)屬性存儲(chǔ)請(qǐng)求(request)的超時(shí)時(shí)間,為了便于實(shí)現(xiàn)此功能,我們給HttpRequestMessage創(chuàng)建一個(gè)擴(kuò)展方法:

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參數(shù)是可null的TimeSpan值,我們現(xiàn)在可以給請(qǐng)求設(shè)置超時(shí)值,但是目前還沒有實(shí)際使用到這段代碼。

Http Handler

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

我們的自己的超時(shí)Handler將繼承DelegatingHandler,DelegatingHandler是一種設(shè)計(jì)為鏈?zhǔn)秸{(diào)用其他Handler的類(簡(jiǎn)單提一下:DelegatingHandler內(nèi)部有個(gè)InnerHandler成員變量,我們可以在調(diào)用innerHandler.SendAsync()前后對(duì)request、CancellationToken和response做相應(yīng)處理)。要實(shí)現(xiàn)我們的Handler,我們重寫SendAsync方法。最小的實(shí)現(xiàn)如下所示:

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

上述代碼并沒有任何用處,因?yàn)橹皇菍?shí)際處理丟給了base.SendAsync,目前還沒有對(duì)TimeoutHandler進(jìn)行任何加工處理,我們將逐步對(duì)其加強(qiáng)擴(kuò)充,以達(dá)到我們的目的。

給Request加上超時(shí)處理

首先,讓我們給TimeoutHandler添加一個(gè)TimeSpan類型的DefaultTimeout屬性,這個(gè)默認(rèn)超時(shí)時(shí)間是給沒有特意設(shè)置超時(shí)時(shí)間的請(qǐng)求使用的:

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

就像HttpClient.Timeout一樣,我們也設(shè)置默認(rèn)超時(shí)時(shí)間為100秒。

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

我們創(chuàng)建一個(gè)CancellationTokenSource,這個(gè)類可以創(chuàng)建和控制CancellationToken。它將根據(jù)超時(shí)時(shí)間來創(chuàng)建:

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
    //不需要?jiǎng)?chuàng)建CTS,因?yàn)椴惶幚沓瑫r(shí)(下面會(huì)講到)
    return null;
  }
  else
  {
    var cts = CancellationTokenSource
      .CreateLinkedTokenSource(cancellationToken);
    cts.CancelAfter(timeout);
    return cts;
  }
}

這里主要關(guān)注兩個(gè)點(diǎn):

  • 如果request超時(shí)值為Timeout.InfiniteTimeSpan,程序并不會(huì)創(chuàng)建CancellationTokenSource,它將不會(huì)被取消,因此節(jié)省了無用的分配。也就是說在這種情況下,我們將不會(huì)處理超時(shí)。
  • 以上相反,我們創(chuàng)建了一個(gè)在指定timeout后被自動(dòng)取消的CancellationTokenSource(因?yàn)檎{(diào)用了CancelAfter)。請(qǐng)注意,這個(gè)CTS連接了傳入?yún)?shù)的cancellationToken,這個(gè)cancellationToken其實(shí)來自SendAsync方法的實(shí)參。這樣做之后,當(dāng)真正的超時(shí)發(fā)生,或者參數(shù)的cancellationToken自身被取消,CTS都會(huì)被取消。如果想要獲取跟多CancellationToken的內(nèi)容,請(qǐng)訪問這篇文章

最后,我們修改下SendAsync方法,應(yīng)用剛剛創(chuàng)建的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);
  }
}

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

通過這一步,我們有了自己的超時(shí)Handler,可以為每個(gè)請(qǐng)求指定不同的超時(shí)時(shí)間。但是,當(dāng)超時(shí)發(fā)生時(shí),我們?nèi)匀恢荒懿东@到TaskCanceledException異常,這個(gè)問題很容易修復(fù)它。

拋出正確的異常

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

  • 如果是,說明這個(gè)cancel是調(diào)用者自身導(dǎo)致的,對(duì)此直接將異常上拋不處理
  • 如果不是,這意味著是因?yàn)槲覀兊某瑫r(shí)導(dǎo)致的cancel,因此,我們將拋出一個(gè)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();
    }
  }
}

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

至此,我們的超時(shí)Handler已經(jīng)完成了,接下來看看怎么使用它

使用Handler

當(dāng)創(chuàng)建一個(gè)HttpClient時(shí),可以指定一個(gè)自己的Handler作為管道(pipeline)的第一個(gè)Handler。如果沒有指定,默認(rèn)使用的是HttpClientHandler,這個(gè)handler直接發(fā)送請(qǐng)求到網(wǎng)絡(luò)上。為了使用我們自己的TimeoutHandler,我們需要先創(chuàng)建它,然后將timeoutHandler指定為httpClient的handler。在timeoutHandler中,指定InnerHandler為我們自己創(chuàng)建的HttpClientHandler,這樣實(shí)際的網(wǎng)絡(luò)請(qǐng)求就委托到了HttpClientHandler中。

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

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

通過將httpclient的timeout設(shè)置為InfiniteTimeSpan來禁用默認(rèn)的超時(shí)設(shè)置,如果不這樣做,默認(rèn)超時(shí)會(huì)干擾我們自己的超時(shí)

現(xiàn)在,我們嘗試發(fā)送一個(gè)設(shè)定了5秒超時(shí)的請(qǐng)求到需要很久才能響應(yīng)的服務(wù)器

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

如果服務(wù)器在5秒內(nèi)響應(yīng)數(shù)據(jù),我們將會(huì)捕獲到一個(gè)TimeoutException,而不是TaskCanceledException,因此事情似乎按預(yù)期進(jìn)行。

為了檢測(cè)cancellation是否正確運(yùn)行,我們傳入一個(gè)在2秒(比超時(shí)實(shí)際小)后會(huì)被取消的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);

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

總結(jié)

通過實(shí)現(xiàn)我們自己的Http Handler,我們可以用一個(gè)智能的timout handler來解決開始我們提出的問題。

這篇文章的所有代碼在這

以上就是c# HttpClient設(shè)置超時(shí)的步驟的詳細(xì)內(nèi)容,更多關(guān)于c# HttpClient設(shè)置超時(shí)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C#簡(jiǎn)單快速的json組件fastJSON使用介紹

    C#簡(jiǎn)單快速的json組件fastJSON使用介紹

    JSON數(shù)據(jù)格式簡(jiǎn)潔,用于數(shù)據(jù)的持久化和對(duì)象傳輸很實(shí)用。最近在做一個(gè)Razor代碼生成器,需要把數(shù)據(jù)庫的表和列的信息修改后保存下來,想到用JSON序列化對(duì)象并保存,需要時(shí)再反序列化成對(duì)象會(huì)簡(jiǎn)單一些
    2012-11-11
  • C#反射應(yīng)用實(shí)例

    C#反射應(yīng)用實(shí)例

    這篇文章主要介紹了C#反射應(yīng)用,實(shí)例分析了通過反射實(shí)現(xiàn)多系統(tǒng)數(shù)據(jù)庫的配置方法,是比較實(shí)用的技巧,需要的朋友可以參考下
    2014-12-12
  • C#實(shí)現(xiàn)一鍵清空控件值的示例代碼

    C#實(shí)現(xiàn)一鍵清空控件值的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用C#語言實(shí)現(xiàn)一鍵清空控件值的功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定幫助,需要的可以參考一下
    2022-09-09
  • C#實(shí)現(xiàn)XOR密碼(異或密碼)的示例代碼

    C#實(shí)現(xiàn)XOR密碼(異或密碼)的示例代碼

    XOR密碼(異或密碼)是一種簡(jiǎn)單的加密算法,它使用異或(XOR)操作來對(duì)明文和密鑰進(jìn)行加密和解密,本文為大家介紹了C#實(shí)現(xiàn)XOR密碼的相關(guān)知識(shí),希望對(duì)大家有所幫助
    2024-01-01
  • 淺談c# 浮點(diǎn)數(shù)計(jì)算

    淺談c# 浮點(diǎn)數(shù)計(jì)算

    本文通過具體的示例給大家演示了下C#中浮點(diǎn)數(shù)運(yùn)算所遇到的問題及解決方法,有需要的小伙伴可以參考下
    2017-09-09
  • WPF實(shí)現(xiàn)手風(fēng)琴式輪播圖切換效果

    WPF實(shí)現(xiàn)手風(fēng)琴式輪播圖切換效果

    這篇文章主要為大家詳細(xì)介紹了WPF實(shí)現(xiàn)手風(fēng)琴式輪播圖切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • C#多線程及同步示例簡(jiǎn)析

    C#多線程及同步示例簡(jiǎn)析

    這篇文章主要為大家詳細(xì)介紹了C#多線程及同步示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • c# 垃圾回收(GC)優(yōu)化

    c# 垃圾回收(GC)優(yōu)化

    這篇文章主要介紹了c# 垃圾回收(GC)優(yōu)化的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c#,感興趣的朋友可以了解下
    2021-02-02
  • C#判斷頁面中的多個(gè)文本框輸入值是否有重復(fù)的實(shí)現(xiàn)方法

    C#判斷頁面中的多個(gè)文本框輸入值是否有重復(fù)的實(shí)現(xiàn)方法

    這篇文章主要介紹了C#判斷頁面中的多個(gè)文本框輸入值是否有重復(fù)的實(shí)現(xiàn)方法,是一個(gè)非常簡(jiǎn)單實(shí)用的技巧,需要的朋友可以參考下
    2014-10-10
  • c# record的使用場(chǎng)景

    c# record的使用場(chǎng)景

    這篇文章主要介紹了c# record的使用場(chǎng)景,幫助大家更好的理解和學(xué)習(xí)使用c#的新特性,感興趣的朋友可以了解下
    2021-02-02

最新評(píng)論