目录

介绍

背景

使用代码

兴趣点


介绍

HttpClient类是经常使用,但也往往不能完全理解。它的行为可能受到DelegationHandler实现的影响,可以通过依赖注入来使用实例,并且可以通过集成测试来测试它的工作方式。本文介绍了这些事情如何工作。

背景

本文适用HttpClient至少使用过一次并希望了解更多信息的.NET Core开发人员。

使用代码

首先,我们要设置HttpClient的创建和依赖注入。在ASP.NET Core应用程序中,这通常是在ConfigureServices方法中完成的。一个HttpClient实例通过依赖注入被注入到一个SearchEngineService实例。两个处理程序管理HttpClient:LogHandler和RetryHandler的行为。这是ConfigureServices实现的样子:

public void ConfigureServices(IServiceCollection services)
{services.AddControllers();services.AddTransient<LogHandler>();services.AddTransient<RetryHandler>();var googleLocation = Configuration["Google"];services.AddHttpClient<ISearchEngineService, SearchEngineService>(c =>{c.BaseAddress = new Uri(googleLocation);}).AddHttpMessageHandler<LogHandler>().AddHttpMessageHandler<RetryHandler>();
}

从上面的代码可以清楚地看到,LogHandler设置在RetryHandler之前。LogHandler是第一个处理程序,因此它处理在调用HttpClient时需要直接发生的事情。这是LogHandler实现:

public class LogHandler : DelegatingHandler
{private readonly ILogger<LogHandler> _logger;public LogHandler(ILogger<LogHandler> logger){_logger = logger;}protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){var response = await base.SendAsync(request, cancellationToken);_logger.LogInformation("{response}", response);return response;}
}

从上面的代码可以清楚地看到,此处理程序实现仅在调用base方法之后记录来自Web请求的响应。此基本方法触发的内容由第二个处理程序设置:RetryHandler。如果服务器意外错误,此处理程序将重试。如果它直接成功或给出3次以上的服务器错误,则最后的结果计数并将返回。

public class RetryHandler : DelegatingHandler
{protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){HttpResponseMessage result = null;for (int i = 0; i < 3; i++){result = await base.SendAsync(request, cancellationToken);if (result.StatusCode >= HttpStatusCode.InternalServerError){continue;}return result;}return result;}
}

如前所述,需要将这些处理程序管理的HttpClient注入到SearchEngineService实例中。这个类只有一个方法。该方法调用HttpClient实例,并返回内容的长度作为响应。

public class SearchEngineService : ISearchEngineService
{private readonly HttpClient _httpClient;public SearchEngineService(HttpClient httpClient){_httpClient = httpClient;}public async Task<int> GetNumberOfCharactersFromSearchQuery(string toSearchFor){var result = await _httpClient.GetAsync($"/search?q={toSearchFor}");var content = await result.Content.ReadAsStringAsync();return content.Length;}
}

SearchEngineService是控制器类的依赖,这个控制器类有一个get方法,它将方法调用的结果作为ActionResult返回。这是控制器类。

[Route("api/[controller]")]
[ApiController]
public class SearchEngineController : ControllerBase
{private readonly ISearchEngineService _searchEngineService;public SearchEngineController(ISearchEngineService searchEngineService){_searchEngineService = searchEngineService;}[HttpGet("{queryEntry}", Name = "GetNumberOfCharacters")]public async Task<ActionResult<int>> GetNumberOfCharacters(string queryEntry){var numberOfCharacters = await _searchEngineService.GetNumberOfCharactersFromSearchQuery(queryEntry);return Ok(numberOfCharacters);}
}

要编写一个集成测试此控制器,我们使用IntegrationFixture(NuGet包这里,文档在这里,文章有一些类似的代码在这里)。外部依赖关系已由模拟服务器代替,该模拟服务器在第一个请求之后返回内部服务器错误,而在第二个请求之后成功。对我们的控制器方法的调用已完成。这会触发对SearchEngineService的调用,从而调用HttpClient。如前所述,此类调用触发对LogHandler的调用,此后触发对RetryHandler的调用。由于第一个调用给出服务器错误,因此重试完成。RetryHandler不触发LogHandler(反之亦然)。因此,我们的应用程序仅记录一个响应,而实际上有两个响应(一个失败和一个成功)。这是我们的集成测试的代码:

[Fact]
public async Task TestDelegate()
{// arrangeawait using (var fixture = new Fixture<Startup>()){using (var searchEngineServer = fixture.FreezeServer("Google")){SetupUnStableServer(searchEngineServer, "Response");var controller = fixture.Create<SearchEngineController>();// actvar response = await controller.GetNumberOfCharacters("Hoi");// assert, externalvar externalResponseMessages = searchEngineServer.LogEntries.Select(l => l.ResponseMessage).ToList();Assert.Equal(2, externalResponseMessages.Count);Assert.Equal((int)HttpStatusCode.InternalServerError, externalResponseMessages.First().StatusCode);Assert.Equal((int)HttpStatusCode.OK, externalResponseMessages.Last().StatusCode);// assert, internalvar loggedResponse = fixture.LogSource.GetLoggedObjects<HttpResponseMessage>().ToList();Assert.Single(loggedResponse);var externalResponseContent = await loggedResponse.Single().Value.Content.ReadAsStringAsync();Assert.Equal("Response", externalResponseContent);Assert.Equal(HttpStatusCode.OK, loggedResponse.Single().Value.StatusCode);Assert.Equal(8, ((OkObjectResult)response.Result).Value);}}
}private void SetupUnStableServer(FluentMockServer fluentMockServer, string response)
{fluentMockServer.Given(Request.Create().UsingGet()).InScenario("UnstableServer").WillSetStateTo("FIRSTCALLDONE").RespondWith(Response.Create().WithBody(response, encoding: Encoding.UTF8).WithStatusCode(HttpStatusCode.InternalServerError));fluentMockServer.Given(Request.Create().UsingGet()).InScenario("UnstableServer").WhenStateIs("FIRSTCALLDONE").RespondWith(Response.Create().WithBody(response, encoding: Encoding.UTF8).WithStatusCode(HttpStatusCode.OK));
}

如果查看上面显示的代码,则会看到两个断言部分。在第一个断言部分中,我们验证外部(模拟)服务器的日志。由于第一个Web请求失败。我们希望执行第二个Web请求(带有第二个响应),因此应该有两个响应,这正是我们验证的结果。

在第二个断言部分中,我们验证应用程序本身的日志。如前所述,仅记录了一个响应,因此我们在第二个断言部分对此进行了验证。

如果您想进一步熟悉,建议在本文中显示的GitHub上下载源代码。例如,您可以更改处理程序的顺序或添加新的处理程序,然后看看会发生什么。通过使用IntegrationFixture进行测试,您可以轻松地验证我们自己的应用程序和外部(模拟)服务器的日志。

兴趣点

在撰写本文和示例代码时,我对HttpClient实际的工作方式有了更好的了解。通过使用处理程序,您不仅可以执行Web请求,还可以做更多的事情。您可以在进行Web请求时构建日志记录,重试机制或其他所需的内容。

理解、创建、使用和测试HttpClient相关推荐

  1. Robot Framework自动化测试教程-通过RIDE创建工程、测试套、测试用例、测试资源、变量文件,引入测试库

    1. 创建测试工程 RIDE工具中有Project概念,实际上Robot Framework中是没有工程的概念,可以理解为最顶层的测试套. 1.1. 新建测试工程 点击 File–>New Pr ...

  2. 使用TestProject Python SDK创建移动Appium测试

    使用模拟器在Android上运行移动网络测试 我将假设您已经下载了Python SDK,已经配置了代理和开发人员令牌,并且代理正在运行.如果没有,您可以在这篇文章中阅读如何做.另外,我将假设您已经创建 ...

  3. JAD项目创建与部署测试

    项目创建与部署测试 版 本:1.0.1                                         拟制人: hechuan 1       前言 JAD旨在为程序员提供一个高效的 ...

  4. (C#)xml的简单理解创建和读取

    xml知识点清理: 一.文档规则 1.区分大小写. 2.属性值必须加引号(单引号.双引号都可以),一般情况下建议使用使用双引号. 3.所有标记必须有结束符号. 4.所有空标记必须关闭. 5.必须有且仅 ...

  5. 一篇文章带你深入理解什么是负载测试

    介绍 任何软件开发项目接近完成的时候,它可能已经通过无数次测试了,特别是在测试和开发同时发生的敏捷测试环境下.无论你已经进行过多少轮测试,一旦你的应用程序已接近完成,那么只有一个办法知道你的软件是否可 ...

  6. 创建 Monitor 并测试 - 每天5分钟玩转 OpenStack(124)

    前面我们创建了 Pool,VIP 并添加了 Member.今天将创建 Monitor,然后测试 LBaaS 是否能够正常工作. 创建 Monitor LBaaS 可以创建 monitor,用于监控 P ...

  7. 数据库修复Part1:创建自己的测试corrupt数据库

    以前看Pual写过很多数据恢复的文章,他很多的测试都是自己创建的Corrupt数据库,其实我们自己也可以. 1. 创建数据库数据表插入数据: use master go create database ...

  8. junit jndi_使用Spring创建用于JUnit测试的JNDI资源

    junit jndi 直到最近,我还使用静态方法来设置内存数据库(HSQLDB). 我在JUnit测试的setUp / tearDown中调用了这些方法. 当我使用Spring时,这对我来说总是有点不 ...

  9. 使用Spring创建用于JUnit测试的JNDI资源

    直到最近,我还使用静态方法来设置内存数据库(HSQLDB). 我在JUnit测试的setUp / tearDown中调用了这些方法. 当我使用Spring时,这总是让我感到不自然,并且所有内容都应在其 ...

最新文章

  1. opencv中的imread不支持中文路径的解决办法
  2. ecshop 后台添加评论_如何提升外卖营业额?评论回复不可忽视
  3. 信息学奥赛一本通(2056:【例3.7】最大的数)
  4. Java 算法 寂寞的数
  5. sap模块介绍_一分钟掌握SAP小知识-系统基本操作
  6. java实现Excel数据导出
  7. Lua面向对象之二:类继承
  8. [HEOI2015]定价 (贪心)
  9. DTcms 上传图片BUG补丁,解决方案
  10. 探讨服务端自定义生成PDF的几种方案
  11. 磁卡、ID卡、IC卡、M1卡、CPU卡的理解区分
  12. 用python画哆啦a梦的头_用 Python 画个哆啦A梦
  13. 墨画子卿第一章第7节: “刀马旦”
  14. 【嵌入式热敏打印模块(1)】
  15. 35 实战 微额借款用户人品预测
  16. Python 中最全面的 Socket 编程指南
  17. 单点登录(一)| LDAP 协议
  18. Delphi 2010 里公开的 Midas.dll 的源代码!!
  19. 素数筛法(传统普通、朴素筛法、埃式筛法、欧拉筛法(线性筛))
  20. Java JDK 下载安装,以及环境配置

热门文章

  1. go 依赖注入 哪个好_go与java的依赖注入实现的一些差异
  2. php echo nbsp,关于include里面的函数echo的问题
  3. 是不正确的python语句_Python if语句读取不正确
  4. zipkin使用_我的Spring Cloud(十):Zipkin 服务跟踪
  5. mysql5权威指南_MySQL5权威指南(第3版)
  6. java 堆 栈 ===_Java堆,栈,堆栈
  7. 高甜预警|甜齁你的情人节促销海报设计模板
  8. 平面设计师必备,剪纸风格素材
  9. 电商夏季促销海报设计PSD模板,分解教你如何设计
  10. java 狗带风波_养狗风波作文