当前位置: 首页 > news >正文

[cpprestsdk] http_client_config | GET | request()

第3章:HTTP客户端(http_client

欢迎回来

在第2章:HTTP消息(http_requesthttp_responsehttp_headers)中,构建了http_request对象(我们的消息)和理解http_response对象(回复)。

我们知道如何指定目标URI、操作(GET、POST)、在头部添加特殊指令以及在正文中包含数据。

但知道如何写信并不意味着能把它送到全国各地,需要一个信使服务。在cpprestsdk中,你的程序在互联网上的信使服务是web::http::client::http_client类。本章将介绍这个关键组件,展示如何实际发送构建的HTTP请求并从Web服务器接收响应

问题:通过网络发送和接收消息

假设已经构建了一个完美的http_request来获取伦敦的天气预报:GET https://api.weather.com/forecast?city=London。你有URI、方法和获取数据的愿望。但这个请求如何从你的C++程序实际传输到api.weather.com服务器,服务器的回复又如何返回到你的应用程序?

手动处理网络套接字、连接重试、超时和数据流是一项复杂且容易出错的任务。你需要:

  1. 解析服务器地址:将api.weather.com转换为IP地址。
  2. 建立连接:打开到该IP地址和端口(HTTP为80,HTTPS为443)的TCP/IP连接。
  3. 发送原始字节:将你的http_request对象转换为字节流并通过连接发送。
  4. 监听回复:等待服务器发送其响应字节。
  5. 解析原始字节:从传入的字节流中重建http_response对象。
  6. 处理错误:如果服务器宕机怎么办?如果连接中断怎么办?如果耗时过长怎么办?

http_client类解决了所有这些麻烦。它提供了一个高级、易用的接口来执行HTTP通信,让你专注于消息内容而不是网络协议的底层细节

介绍http_client:网络信使服务

web::http::client::http_client是发送HTTP请求的核心类。创建一个http_client对象,通常配置一个基础URI(通过该客户端发送的所有请求的共同起始地址)。

然后使用它的request()方法发送http_request对象。

以下是其关键组件的分解:

  1. 构造函数(http_client(uri base_uri, ...):通过提供你想与之通信的Web服务的基础地址来创建http_client。这通常是协议(http/https)和域名(例如https://api.weather.com)。
  2. request()方法:这是核心方法。你传递给它一个http_request对象,它返回一个pplx::task<http_response>。这个pplx::task是一个特殊的cpprestsdk对象,表示一个将异步完成的操作(意味着你的程序可以在等待时做其他事情)。我们将在第4章:PPLX任务(异步编程模型)中详细介绍pplx::task,但现在我们将使用简单的.get()调用来等待结果。
  3. http_client_config(可选):这个类允许你自定义http_client的各种设置,如超时、代理设置和证书验证。

让我们深入探讨如何发送请求

使用http_client发送第一个Web请求

我们将使用我们的天气预报示例。我们的目标是从https://api.weather.com/forecast?city=London&days=3获取数据。

#include "cpprest/http_client.h" // 用于http_client和http_client_config
#include "cpprest/uri.h"         // 用于第1章的uri
#include "cpprest/http_msg.h"    // 用于第2章的http_request, http_response
#include <iostream>int main() {// 1. 定义天气API的基础URI// 这是地址的共同部分。web::uri base_api_uri(U("https://api.weather.com"));// 2. 创建一个http_client对象// 它被配置为与'https://api.weather.com'通信web::http::client::http_client client(base_api_uri);// 3. 定义我们特定预报的相对路径和查询// 这部分附加到基础URI上。web::uri_builder uri_path_builder(U("/forecast"));uri_path_builder.append_query(U("city"), U("London"));uri_path_builder.append_query(U("days"), 3);// 4. 创建一个http_request对象(如第2章所学)web::http::http_request request(web::http::methods::GET);request.set_request_uri(uri_path_builder.to_uri()); // 设置完整的相对URIstd::wcout << L"发送请求到: " << client.base_uri().to_string() << request.relative_uri().to_string() << std::endl;// 5. 发送请求并等待响应// client.request()返回一个'pplx::task'。.get()等待其完成。pplx::task<web::http::http_response> response_task = client.request(request);// 为了演示,我们将阻塞并等待响应// (更多关于任务的异步编程在第4章!)web::http::http_response response = response_task.get();// 6. 处理响应(如第2章所学)std::wcout << L"收到响应,状态码: " << response.status_code() << std::endl;// 检查请求是否成功(HTTP 200 OK)if (response.status_code() == web::http::status_codes::OK) {// 将响应的正文提取为字符串utility::string_t body = response.extract_string().get();std::wcout << L"响应正文:\n" << body << std::endl;} else {std::wcout << L"请求失败,错误: " << response.reason_phrase() << std::endl;}return 0;
}

运行这段代码会发生什么?
如果api.weather.com是一个真实可访问的API,返回伦敦3天预报的JSON,你可能会看到如下输出(实际输出取决于真实API):

发送请求到: https://api.weather.com/forecast?city=London&days=3
收到响应,状态码: 200
响应正文:
{"city":"London","forecast":[{"day":"Today","temp":"15C","conditions":"Cloudy"}, {"day":"Tomorrow","temp":"18C","conditions":"Sunny"}]}

这个简单示例展示了整个流程:

  1. 我们定义了目标Web服务的base_uri
  2. 我们创建了一个http_client来与该服务通信。
  3. 我们构建了预报请求的特定uri
  4. 我们创建了一个http_request对象,设置其方法和目标URI。
  5. 我们使用client.request(request).get()发送请求并阻塞直到收到响应。
  6. 最后,我们检查http_response的状态码并提取其正文。

关于相对URI的说明

注意,当我们创建http_client时,我们给它https://api.weather.com。然后,当我们设置http_request的URI时,我们提供/forecast?city=London&days=3

http_client巧妙地组合了这些:

  • 它使用自己的base_urihttps://api.weather.com)。
  • 它附加http_requestrelative_uri/forecast?city=London&days=3)。
  • 结果是网络上发送的完整URI:https://api.weather.com/forecast?city=London&days=3

这种模式允许你为一个服务创建一个http_client,然后向该服务的不同“端点”(路径)发送多个请求,而无需重复完整的基础地址。

使用http_client_config自定义http_client

web::http::client::http_client_config类允许你微调客户端的行为。常见的配置是设置超时。默认情况下,操作可能会无限期等待或等待很长的默认时间,这对于响应式应用程序来说并不理想。

以下是你可以为请求设置超时的方法:

#include "cpprest/http_client.h"
#include "cpprest/uri.h"
#include "cpprest/http_msg.h"
#include <iostream>
#include <chrono> // 用于std::chrono::secondsint main() {web::uri base_api_uri(U("http://example.com")); // 使用example.com作为占位符// 1. 创建一个http_client_config对象web::http::client::http_client_config config;// 2. 将超时设置为5秒// 如果服务器在5秒内没有响应,请求将失败。config.set_timeout(std::chrono::seconds(5));// 3. 使用我们的自定义配置创建http_clientweb::http::client::http_client client(base_api_uri, config);// 4. 创建并发送一个简单的GET请求web::http::http_request request(web::http::methods::GET);request.set_request_uri(U("/slow_endpoint")); // 假设这个端点非常慢std::wcout << L"发送带有5秒超时的请求..." << std::endl;try {web::http::http_response response = client.request(request).get();std::wcout << L"收到响应,状态码: " << response.status_code() << std::endl;} catch (const web::http::http_exception& e) {// 超时可能会抛出http_exceptionstd::wcout << L"请求因HTTP异常失败: " << e.what() << std::endl;} catch (const std::exception& e) {std::wcout << L"请求因一般异常失败: " << e.what() << std::endl;}return 0;
}

运行这段代码会发生什么?
如果http://example.com/slow_endpoint响应时间超过5秒,你可能会看到如下输出:

发送带有5秒超时的请求...
请求因HTTP异常失败: 连接尝试失败,因为连接方在一段时间后未正确响应,或建立的连接失败,因为连接的主机未能响应

(确切的错误消息可能因操作系统和网络条件而异)。

这展示了http_client_config如何让你控制客户端的行为,这对于构建健壮的应用程序至关重要。其他有用的设置包括set_proxy()用于代理服务器详情,以及set_validate_certificates()用于控制SSL证书检查。

http_client的工作原理

当你创建一个http_client并发送请求时,幕后会发生一系列复杂的步骤来管理网络通信。

概述

  1. 客户端初始化:当你用base_uriconfig创建http_client时,cpprestsdk首先验证URI(例如检查"http://“或"https://”)。然后设置一个内部“管道”或处理程序链来处理你的请求。这个管道的最后一个阶段是平台特定的网络通信器(例如在Windows上使用WinHTTP,在其他平台上使用Boost.Asio)。
  2. 请求准备:当你调用client.request(your_http_request_obj)时,http_client准备你的请求:
    • 添加默认的User-Agent头部(例如cpprestsdk/2.10.0)。
    • 将其base_uri与你的请求的relative_uri组合成最终的绝对URI。
    • 关联一个pplx::cancellation_token,允许你稍后取消请求。
  3. 管道处理:准备好的http_request然后开始在客户端的内部管道中移动。管道的每个阶段可以检查、修改甚至短路请求/响应。例如,OAuth处理程序可能添加授权头部,或重定向处理程序可能自动跟随HTTP重定向。
  4. 网络传输:最终,请求到达平台特定的网络通信阶段。这个组件处理:
    • DNS解析:查找目标主机的IP地址(例如api.weather.com)。
    • TCP连接:建立原始网络连接。对于HTTPS,它还执行SSL/TLS握手。
    • 发送请求:将http_request的方法、URI、头部和正文作为原始字节写入网络流。
    • 接收响应:读取服务器响应的原始字节。
    • 解析响应:将原始响应字节转换为http_response对象(状态码、头部、正文流)。
    • 错误/超时处理:如果出现任何问题(例如网络错误、连接超时),它会捕获问题并将其转换为你的pplx::task的异常。
  5. 任务完成:一旦http_response完全接收(或发生错误),与你的client.request()调用关联的pplx::task完成,你可以检索http_response(或捕获异常)。

以下是GET请求的简化序列图:

在这里插入图片描述

http_client代码

让我们看看http_client如何构造以及如何处理request()的简化版本。

http_client构造函数

当你创建http_client时,它主要设置基础URI和内部通信管道。

文件:Release/src/http/client/http_client.cpp

// 简化自cpprestsdk源码
http_client::http_client(const uri& base_uri, const http_client_config& client_config) {std::shared_ptr<details::_http_client_communicator> final_pipeline_stage;// 验证URI方案(http/https)和主机。// 如果方案缺失,可能会添加默认值(例如"http")。if (base_uri.scheme().empty()) {// ... (添加默认方案的逻辑) ...} else {verify_uri(base_uri); // 确保URI对HTTP客户端有效}// 创建实际的网络通信层(例如WinHTTP、Boost.Asio)final_pipeline_stage = details::create_platform_final_pipeline_stage(uri(base_uri), http_client_config(client_config));// 'm_pipeline'管理处理程序链(如认证、重定向)// 'final_pipeline_stage'是网络I/O前的最后一步。m_pipeline = std::make_shared<http_pipeline>(std::move(final_pipeline_stage));// 如果配置了,添加内置处理程序如OAuth。// ...
}

这个构造函数确保你的base_uri有效,并初始化负责实际数据传输的核心网络组件(_http_client_communicator)。m_pipelinecpprestsdk可扩展性的关键部分,允许在发送请求之前进行中间步骤(处理程序)。

http_client::request()方法

这个方法负责接收你的http_request,准备它,并启动其在管道中的旅程。

文件:Release/src/http/client/http_client.cpp

// 简化自cpprestsdk源码
pplx::task<http_response> http_client::request(http_request request, const pplx::cancellation_token& token) {// 如果用户没有设置User-Agent头部,添加一个默认的。if (!request.headers().has(header_names::user_agent)) {request.headers().add(header_names::user_agent, U("cpprestsdk/X.Y.Z")); // 简化版本字符串}// 在请求上设置基础URI(用于解析相对URI)。request._set_base_uri(base_uri());// 将取消令牌附加到请求上。request._set_cancellation_token(token);// 通过内部管道传播请求。// 这是请求开始其异步旅程的地方。return m_pipeline->propagate(request);
}

request()方法确保每个请求都有必要的头部和正确的基础URI上下文。m_pipeline->propagate(request)调用是进入异步处理和网络通信逻辑的入口点。返回的pplx::task<http_response>最终将保存服务器的回复或异常(如果出现问题)。

总结

在本章中,我们学习了如何使用cpprestsdk通过网络实际发送HTTP消息:

特性web::http::client::http_clientweb::http::client::http_client_config
目的程序发送HTTP请求的信使配置http_client的行为。
初始化使用base_uri和可选的http_client_config默认构造函数,然后设置属性。
核心操作request(http_request)发送请求。set_timeout()set_proxy()等。
返回pplx::task<http_response>配置未来http_client的行为。
类比邮政服务或快递公司。快递员的指令(例如“5秒内送达”)。

现在掌握了以下工具:

  1. 使用web::uri定义Web服务的基础地址。
  2. 使用http_client_config配置客户端的行为。
  3. 创建http_client实例。
  4. 构建http_request对象。
  5. 使用client.request()发送这些请求并接收http_response对象。

request()方法返回一个pplx::task,这是cpprestsdk异步特性的基础。

虽然我们使用.get()来简化示例,但理解如何与这些任务配合是编写高效、非阻塞C++应用程序的关键

在下一章中,我们将全面探索pplx::taskpplx::task_completion_event,这是cpprestsdk强大的异步编程模型,允许程序执行网络操作而不会冻结应用程序

http://www.dtcms.com/a/469630.html

相关文章:

  • HQChart使用教程30-K线图如何对接第3方数据46-DRAWTEXT_FIX数据结构
  • 奥特蛋的做网站门户网站的发布特点
  • Unity网络开发--超文本传输协议Http(2)
  • Java后端开发核心技术选型指南(优化版)
  • 数字图像处理-图像的傅里叶变换
  • 分布式缓存架构:从原理到生产实践
  • 现代计算机视觉任务综合概述:定义、方法与应用
  • ai 作物分类
  • sm2025 模拟赛11 (2025.10.7)
  • WebSocket 是什么原理?为什么可以实现持久连接?
  • 【开题答辩全过程】以 宝成花园物业管理系统为例,包含答辩的问题和答案
  • CORS配置实战:SpringBoot与Vite项目的本地联调解决方案
  • 长沙学做网站建设wordpress图片全部压缩
  • [cpprestsdk] 统一资源标识符 | uri_builder
  • 网站开发与运维收费明细报社网站开发做什么
  • 徐州做网站的公司招聘可以做ppt的网站或软件
  • python自带的unittest框架
  • STM32学习记录-0.1 STM32外设
  • 人形机器人:Tesla Optimus的AI集成细节
  • starocks创建表后还需要设置什么
  • 《操作系统真象还原》 第十章 输入输出系统
  • 免费发布信息的网站网站建设规划文档
  • kali安装ARL-docker灯塔
  • Linux的Dynamic debug功能
  • 需要做网站建设的公司做流程图用什么网站
  • 常用的日期时间处理库Day.js和Moment.js
  • Verilog和FPGA的自学笔记5——三八译码器(case语句与锁存器)
  • Mpi多机通信环境搭建(2台机器)
  • 简述网站制作流程图如何免费注册淘宝店铺
  • 人工智能在数学教育中的应用 | 现状、探索与实践