返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >c# HttpClient设置超时的步骤
  • 559
分享到

c# HttpClient设置超时的步骤

2024-04-02 19:04:59 559人浏览 八月长安
摘要

目录问题 为每个request设置超时值 Http Handler 给Request加上超时处理 抛出正确的异常 使用Handler 总结 HttpClient作为官方推荐的http

HttpClient作为官方推荐的http客户端,相比之前的WEBClient和WebRequest好用了很多,但默认无法为每个请求单独设置超时,只能给HttpClient设置默认超时,使用起来不太方便。

声明:本文主要是翻译自THOMAS LEVESQUE'S .net BLOG的文章:Better timeout handling with HttpClient。
由于工作原因,需要用C#,就语法层而言,c#确实比java优秀,一些库接口封装也更方便简洁。特别是HttpClient,结合了task异步模型,使用起来非常顺手。
本人水平有限,如有问题,还望各位多多海涵,不吝赐教

问题

如果你经常用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,这正是我们期望的。

总结

通过实现我们自己的Http Handler,我们可以用一个智能的timout handler来解决开始我们提出的问题。

这篇文章的所有代码在这

以上就是c# HttpClient设置超时的步骤的详细内容,更多关于c# HttpClient设置超时的资料请关注编程网其它相关文章!

--结束END--

本文标题: c# HttpClient设置超时的步骤

本文链接: https://lsjlt.com/news/121895.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • c# HttpClient设置超时的步骤
    目录问题 为每个request设置超时值 Http Handler 给Request加上超时处理 抛出正确的异常 使用Handler 总结 HttpClient作为官方推荐的http...
    99+
    2024-04-02
  • 怎么在c#中设置HttpClient超时
    这篇文章将为大家详细讲解有关怎么在c#中设置HttpClient超时,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。问题如果你经常用HttpClient去调用Restfull接口或传送文件,你...
    99+
    2023-06-14
  • 详解Nginx的超时keeplive_timeout配置步骤
    目录keepalive_timeoutclient_body_timeoutclient_header_timeoutsend_timeoutNginx 处理的每个请求均有相应的超时...
    99+
    2024-04-02
  • 教程:Golang设置时区步骤
    如题所示,以下是一篇关于Golang设置时区的教程,包含具体的代码示例。 一、背景介绍 在开发Golang应用程序时,经常需要设置正确的时区以保证时间表现的准确性。Golang中的ti...
    99+
    2024-02-28
    golang 步骤 时区设置
  • linux设置定时任务的方法步骤
    一,首先登录 二,找到文件夹 三,查看定时任务 crontab -l 四,vi root 编辑定时任务 编辑完成后,点ESC,然后:wq 时间格式 分钟 小时 日期 月份 周 命令 数字范围 0-59 0-23 1...
    99+
    2022-06-04
    linux设置定时任务 linux定时任务
  • golang设置超时
    Golang是一种高效的编程语言,被广泛应用于各种领域的开发中。在一些特定场景下,我们需要设置超时来避免程序的死锁或卡死。在本文中,我将详细介绍Golang如何设置超时,以及几种设置超时的方式。一、使用channel在Golang中,可以使...
    99+
    2023-05-16
  • linux手动设置时间的步骤是什么
    要在Linux中手动设置时间,可以按照以下步骤进行: 打开终端或控制台。 使用root权限登录或使用sudo命令以管理员身份...
    99+
    2023-10-26
    linux
  • PHP设置时区为中国的步骤详解
    如何在PHP中设置时区为中国 PHP中设置时区是一个常见的操作,特别是在处理日期和时间的应用中。在PHP中,我们可以通过设置时区来确保日期和时间的准确性,以及在不同时区下的正确显示。下...
    99+
    2024-04-02
  • c#中nmodbus读写超时怎么设置
    在C#中使用nModbus库进行Modbus读写操作时,可以通过设置Timeout属性来设置超时时间。以下是一个示例代码: usin...
    99+
    2024-04-02
  • php curl 设置超时
    在 PHP 中使用 cURL 函数发送请求时,我们可以通过设置超时时间来控制请求的最大时间限制,以避免长时间等待服务器响应而阻塞 PHP 脚本。 cURL 函数提供了两个选项来设置超时时间:CURLO...
    99+
    2023-09-02
    php 服务器 开发语言
  • golang http 超时设置
    Go语言是一种支持并发编程的高效编程语言,因此它在网络编程方面表现突出。HTTP协议是构建互联网应用程序的重要组成部分,Go语言中的http包也提供了大量的接口来支持HTTP请求和响应。在进行HTTP网络调用时,超时配置对于保证程序的稳定性...
    99+
    2023-05-15
  • win7定时关机怎么设置的步骤教程
    作为上班族,总有一些忙里忙外的,跑前跑后,有些职员忙的昏天黑地的,难免会出现win7系统电脑没有关机就下班的情况,这样win7系统电脑会一直开机到第二天上班,很不低碳,那么为了避免这种情况,我们可以设置定时关机,下面来看看win7定时关机怎...
    99+
    2023-07-15
  • Golang自带的HttpClient超时机制怎么实现
    本篇内容主要讲解“Golang自带的HttpClient超时机制怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang自带的HttpClient超时机制怎么实现”吧!Java Htt...
    99+
    2023-07-04
  • Win10 2004时间设置为长日期的方法步骤
    方法如下:1、右键右下角任务栏的时间,会出现菜单栏,选择调整日期/时间;2、点击日期、时间和区域格式设置;3、找到相关设置,选择其他日期、时间和区域设置;4、选择更改日期、时间或数字格式;5、点击其他设置;6、选择日期选项卡,在这里就可以任...
    99+
    2023-06-05
  • python 设置方法超时
    #!/usr/bin/python #-*-coding:utf-8-*- import os,time,signal,platform,subprocess class TimeoutError(Exception): pas...
    99+
    2023-01-31
    方法 python
  • mysql怎么设置session的超时时间
    本篇内容介绍了“mysql怎么设置session的超时时间”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • php.ini怎么设置超时时间
    本教程操作环境:windows7系统、PHP7.1版,DELL G3电脑php.ini 中缺省的最长执行时间是 30 秒,这是由 php.ini 中的 max_execution_time 变量指定,倘若你有一个需要颇多时间才能完成的工作,...
    99+
    2021-08-07
    php.ini 超时时间
  • SpringBoot设置接口超时时间
    SpringBoot设置接口访问超时时间有两种方式一、在配置文件application.properties中加了spring.mvc.async.request-timeout=20000,意思是设置超时时间为20000ms即20s,二、...
    99+
    2015-11-02
    java教程 SpringBoot
  • Nginx设置HTTPS的方法步骤
    目录背景HTTPHTTPS配置过程域名证书申请编辑nginx.conf文件背景 HTTP 超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据...
    99+
    2024-04-02
  • JayProxy的设置步骤是什么
    以下是使用JayProxy的设置步骤:1. 首先,下载和安装JayProxy应用程序。你可以在JayProxy的官方网站上找到应用程...
    99+
    2023-09-12
    JayProxy
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作