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

[cpprestsdk] 构建HTTP消息 | http_headers.h

第2章:HTTP消息(http_requesthttp_responsehttp_headers

欢迎回来

在第1章:URI与URI构建器(uriuri_builder)中,我们学习了如何使用uriuri_builder精确定位互联网上的资源

可以将URI想象成信封上写的邮政地址。但信封里的信件包裹是什么?如何告诉邮局(Web服务器)想发送什么或期望什么样的回复?

这正是HTTP消息的作用

cpprestsdk中,http_requesthttp_responsehttp_headers类代表了这些网络通信的基本组成部分。本章将介绍这些关键组件,展示如何构建发送的请求以及如何解析收到的响应

相关前文传送:
[APItest-Karate] HttpRequestBuilder | HttpClient发送请求
[项目][boost搜索引擎#4] cpp-httplib使用 | log.hpp | 前端 | 测试及总结

问题:封装网络通信

再次想象我们的天气应用。我们知道获取伦敦天气预报的URI:https://api.weather.com/forecast?city=London&days=3。这是我们的目标地址。但仅仅有地址是不够的。我们需要告诉天气服务器:

  1. 我们想做什么:是获取数据?发送新数据?删除某些内容?(这是HTTP方法)。对于天气数据,我们通常使用GET
  2. 任何特殊指令:我们是否偏好特定语言的响应?接受什么格式?(这些是HTTP头部
  3. 我们发送的数据:如果我们要向数据库添加新城市,会在消息主体(HTTP正文)中包含该城市的详细信息。

当天气服务器回复时,它也会发送一个HTTP消息。我们需要能够:

  1. 检查是否成功:请求是否成功,或者是否有错误?(这是HTTP状态码)。
  2. 理解回复:服务器返回了什么?例如天气预报数据本身(这是HTTP正文)。
  3. 读取额外信息:天气预报是什么格式的?何时生成的?(更多HTTP头部)。

cpprestsdk提供了http_requesthttp_responsehttp_headers类来优雅地管理所有这些细节

http_request:发送消息

在这里插入图片描述

web::http::http_request类表示从你的应用程序发送到Web服务器的消息。它包含了服务器理解你需求所需的一切。

http_request的关键部分

  • 方法:想执行的操作(例如GETPOSTPUTDELETE)。cpprestsdkweb::http::methods中提供了这些常量。
  • URI:目标资源的地址(我们在第1章学过!)。
  • 头部:关于请求的元数据,如内容类型、接受的语言等。
  • 正文:你发送的实际数据负载(例如JSON数据、文件)。

让我们为天气示例创建一个简单的GET请求:

#include "cpprest/http_msg.h" // 包含http_request, http_response, http_headers
#include "cpprest/uri.h"      // 来自第1章
#include <iostream>int main() {// 1. 为天气预报创建URIweb::uri weather_uri(U("https://api.weather.com/forecast?city=London&days=3"));// 2. 用GET方法创建HTTP请求对象// cpprestsdk提供了预定义方法如methods::GET, methods::POST等web::http::http_request request(web::http::methods::GET);// 3. 为请求设置URIrequest.set_request_uri(weather_uri);// 现在,看看我们的请求是什么样子的(用于调试)std::wcout << L"我们的请求:\n" << request.to_string() << std::endl;return 0;
}

输出:

我们的请求:
GET https://api.weather.com/forecast?city=London&days=3 HTTP/1.1

在这段代码中:

  • web::http::methods::GET是表示GET HTTP方法的常量
  • request.set_request_uri(weather_uri)将目标地址分配给我们的请求
  • request.to_string()是获取请求字符串表示的便捷方式,非常适合调试

http_request添加正文

并非所有请求都有正文。GET请求通常没有。但用于发送新数据(如提交表单)的POST请求几乎总是有正文。

以下是一个创建带有简单字符串正文的POST请求的示例:

#include "cpprest/http_msg.h"
#include "cpprest/uri.h"
#include <iostream>int main() {web::uri api_uri(U("https://api.example.com/users"));// 创建POST请求web::http::http_request post_request(web::http::methods::POST);post_request.set_request_uri(api_uri);// 设置简单的字符串正文。cpprestsdk自动将Content-Type设置为text/plainpost_request.set_body(U("username=Alice&email=alice@example.com"));std::wcout << L"我们的POST请求:\n" << post_request.to_string() << std::endl;return 0;
}

输出:

我们的POST请求:
POST https://api.example.com/users HTTP/1.1
Content-Type: text/plain; charset=utf-8
Content-Length: 35username=Alice&email=alice@example.com

注意cpprestsdk在我们调用set_body时自动添加了Content-Type头部和Content-Length头部

这是cpprestsdk提供的众多便利之一。接下来我们将更详细地了解这些头部。

http_headers:消息的信封备注

http_requesthttp_response对象都有一个http_headers成员。这个类充当HTTP头部字段的键值存储。头部提供了关于消息的重要元数据,例如:

  • Content-Type:正文中数据的类型(例如application/jsontext/html)。
  • Authorization:访问受保护资源的凭据。
  • User-Agent:关于发起请求的客户端的信息。
  • Accept:客户端在响应中偏好的数据格式。
  • Content-Length:消息正文的大小(以字节为单位)。

http_headers类处理大小写(Content-Typecontent-type在内部被视为相同),并允许你添加、删除和检索头部值

在这里插入图片描述

实际应用中,http_requesthttp_response不会共享同一个http_headers实例;每个都会有自己独立的http_headers实例

操作头部

让我们为天气请求添加一些自定义头部:

#include "cpprest/http_msg.h"
#include "cpprest/uri.h"
#include <iostream>int main() {web::uri weather_uri(U("https://api.weather.com/forecast?city=London&days=3"));web::http::http_request request(web::http::methods::GET);request.set_request_uri(weather_uri);// 获取请求头部的引用web::http::http_headers& headers = request.headers();// 添加一些头部headers.add(U("Accept"), U("application/json")); // 我们偏好JSON数据headers.add(U("X-Custom-App-Id"), U("myWeatherApp123")); // 自定义头部// 也可以用[]操作符设置,但'add'对多值头部更安全headers[web::http::header_names::accept_language] = U("en-US,en;q=0.9"); // ^ header_names提供了常见头部的常量std::wcout << L"带自定义头部的请求:\n" << request.to_string() << std::endl;// 检索头部值utility::string_t accept_header;if (headers.match(U("Accept"), accept_header)) { // match()是安全的检索方式std::wcout << L"请求的Accept类型: " << accept_header << std::endl;}return 0;
}

输出:

带自定义头部的请求:
GET https://api.weather.com/forecast?city=London&days=3 HTTP/1.1
Accept: application/json
Accept-Language: en-US,en;q=0.9
X-Custom-App-Id: myWeatherApp123请求的Accept类型: application/json

http_headers的关键点:

  • request.headers()给你一个可变的头部引用。
  • headers.add(key, value)添加一个头部。如果头部已存在,通常会追加新值并用逗号分隔(例如Accept头部)。
  • headers[key] = value设置一个头部,覆盖任何先前的值。
  • headers.match(key, output_value)是检索头部值的首选方式,安全地检查是否存在。
  • web::http::header_names提供了常见HTTP头部名称的常量,避免拼写错误。

http_response:你的接收回复

在这里插入图片描述

web::http::http_response类表示你从Web服务器收到的回复消息。它告诉你发生了什么,并提供请求的数据。

http_response的关键部分

  • 状态码:表示结果的数字代码(例如200表示OK,404表示未找到)。cpprestsdkweb::http::status_codes中提供了这些常量。
  • 原因短语:状态码的简短、人类可读描述(例如"OK"对应200)。
  • 头部:关于响应的元数据(例如Content-TypeDateServer)。
  • 正文:服务器返回的实际数据负载(例如天气预报JSON)。

由于我们尚未介绍发送请求(这是第3章:HTTP客户端(http_client)的内容!),我们将创建一个模拟的http_response来演示如何检查其部分内容。

#include "cpprest/http_msg.h"
#include <iostream>
#include <vector>int main() {// 1. 模拟天气服务器的响应// 创建一个状态码为200 OK的响应web::http::http_response response(web::http::status_codes::OK);// 2. 为模拟响应设置一些头部response.headers().add(U("Content-Type"), U("application/json"));response.headers().add(U("Server"), U("WeatherAPI/1.0"));// 3. 为模拟响应设置JSON正文utility::string_t json_body_str = U(R"({"city": "London", "temperature": "15C", "conditions": "Cloudy"})");response.set_body(json_body_str, U("application/json")); // 显式指定内容类型// 现在,检查响应std::wcout << L"模拟响应:\n" << response.to_string() << std::endl;// 访问状态码和原因短语std::wcout << L"状态码: " << response.status_code() << std::endl;std::wcout << L"原因短语: " << response.reason_phrase() << std::endl; // 默认为"OK"对应200// 访问头部utility::string_t content_type_header;if (response.headers().match(U("Content-Type"), content_type_header)) {std::wcout << L"响应Content-Type: " << content_type_header << std::endl;}// 访问正文(返回一个任务,将在第4章介绍)// 现在,我们阻塞以立即获取结果用于演示utility::string_t body_content = response.extract_string().get();std::wcout << L"响应正文:\n" << body_content << std::endl;return 0;
}

输出:

模拟响应:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 68
Server: WeatherAPI/1.0{"city": "London", "temperature": "15C", "conditions": "Cloudy"}
状态码: 200
原因短语: OK
响应Content-Type: application/json
响应正文:
{"city": "London", "temperature": "15C", "conditions": "Cloudy"}

这里我们看到:

  • web::http::status_codes::OK是200状态码的常量。
  • response.status_code()response.reason_phrase()给你基本结果。
  • response.headers().match()http_request中的用法相同。
  • response.extract_string().get()是你将正文作为字符串检索的方式。.get()部分同步等待正文完全可用(更多关于任务的内容在第4章:PPLX任务(异步编程模型))。cpprestsdk还提供了extract_json()extract_vector()用于不同的正文类型。

幕后:HTTP消息的工作原理

http_requesthttp_response的核心是围绕内部实现类(如_http_request_http_response)的轻量级包装器。这些内部类保存URI、方法、状态码、原因短语的实际数据成员,以及重要的http_headers对象和消息正文流。

创建http_request

当你创建http_request对象时:

  1. 对象创建web::http::http_request request(web::http::methods::GET);
    • 这个构造函数实际上在堆上分配了一个内部的details::_http_request对象,使用std::shared_ptr。所有核心数据(方法、URI、头部、正文流)都存储在这个内部对象中。
    • HTTP方法(GET)直接存储。
    • 创建一个空的http_headers对象并与请求关联。
  2. 设置URIrequest.set_request_uri(weather_uri);
    • uri对象(来自第1章)简单地存储在内部的_http_request结构中。
  3. 添加头部request.headers().add(U("Accept"), U("application/json"));
    • headers()方法返回内部http_headers对象的引用。
    • 当调用add()时,头部名称和值存储在http_headers对象内部的std::map中。这个映射使用特殊的比较器来处理头部名称的大小写不敏感(例如"Content-Type"和"content-type"被视为相同头部)。
  4. 设置正文request.set_body(U("..."));
    • 字符串数据被转换为异步流(concurrency::streams::istream)。
    • 然后将这个流设置为_http_request对象的内部正文流(m_inStream)。
    • 关键的是,Content-TypeContent-Length头部会根据正文内容自动添加/更新到http_headers对象中。

以下是事件的简化序列:

在这里插入图片描述

简化的http_headers::add

add方法是一个模板函数,可以接受各种类型的值。它将值转换为字符串并存储,如果头部已存在则处理连接。

// 简化自Release/include/cpprest/http_headers.h
class http_headers
{
private:// 头部存储在键不区分大小写的映射中typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> inner_container;inner_container m_headers;public:template<typename _t1>void add(const key_type& name, const _t1& value){// 将值(如int, string)转换为string_tauto printedValue = utility::conversions::details::print_string(value);auto& mapVal = m_headers[name]; // 访问或创建映射中的条目if (mapVal.empty()){mapVal = std::move(printedValue); // 如果是新的,直接存储}else{// 如果已存在,用逗号追加(某些头部的标准)mapVal.append(_XPLATSTR(", ")).append(std::move(printedValue));}}// ... 其他方法
};

这段代码展示了http_headers如何在内部使用std::map_case_insensitive_cmp结构确保"Accept"和"accept"被视为相同的键,使头部处理更加健壮。

简化的http_request::to_string

to_string方法是调试的便捷方式。它组装请求行,然后调用基类http_msg_base::to_string()添加头部和正文。

// 简化自Release/src/http/client/http_client_msg.cpp
utility::string_t details::_http_request::to_string() const
{utility::string_t result(m_method); // 以HTTP方法开始(如"GET")result += _XPLATSTR(' ');           // 添加空格// 追加URIif (this->m_uri.is_empty()){result += _XPLATSTR('/'); // 如果URI为空,默认为"/"}else{result += this->m_uri.to_string(); // 追加URI字符串}result += _XPLATSTR(" HTTP/1.1\r\n"); // 添加HTTP版本和换行result += http_msg_base::to_string(); // 追加扁平化的头部和正文return result;
}

这说明了to_string如何简单地将HTTP消息的各个部分连接成标准字符串格式。

总结

在本章中,我们通过探索cpprestsdk中的核心消息类型,为HTTP通信奠定了基础:

特性web::http::http_requestweb::http::http_responseweb::http::http_headers
目的发送到服务器的消息从服务器接收的消息任一消息的元数据(键值对)
关键内容方法、URI、头部、正文状态码、原因短语、头部、正文std::string_t键值对的集合
创建方式http_request(method)http_response(status_code)通过request.headers()response.headers()访问
常见操作set_request_uri(), set_body(), headers()status_code(), reason_phrase(), extract_string(), headers()add(key, value), match(key, &value), operator[]
类比你发送的带有指令的信件/包裹你收到的回复信件/包裹信封或运输标签上的备注

现在知道如何构建准备发送的http_request以及如何准备解析http_response。可以指定方法、目标URI、正文内容,并为请求和响应自定义头部。

理解如何构建HTTP消息至关重要,但这些消息不会自己神奇地在互联网上传输!在下一章中,我们将介绍http_client类,这是你实际发送这些http_request对象并从Web服务接收http_response对象的主要工具。准备好发起你的第一个网络调用吧~

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

相关文章:

  • SCI论文写作:从实验设计到发表(选题、文献调研、实验设计、数据分析、论文结构及语言规范)
  • 西安哪里有做网站的网页界面ps制作步骤
  • 《彻底理解C语言指针全攻略(2)》
  • JavaScript 性能优化实战:从原理到落地
  • 网上公司注册申请的流程江西短视频搜索seo推荐
  • 网站建设哪家好知道数字化档案馆及网站的建设
  • 汽车行业密钥灌装解决方案:构建可信的车载安全启动与通信体系
  • Vue2+Django TodoList项目跨域解决方案实战
  • 网页结构解析入门:HTML、CSS、JS 与爬虫的关系
  • Mac查看本机发出请求的IP地址
  • 《基于 YOLOv11 的武器装备视觉检测系统构建与专 利申请指南》
  • 云原生时代:微服务架构与Serverless实践指南
  • 3dgs Scene详解
  • 韩国网站设计风格网页即时聊天
  • 用 Jetpack Compose 实现仿网易云音乐播放页 + 歌词滚动
  • 既然根据时间可推算太阳矢量,为何还需要太阳敏感器?
  • 做娱乐新闻的网站有哪些网站建设教材
  • ORACLE数据库字符集
  • 本机做网站服务上传到凡科手机网站建设开发
  • 谷歌和IBM:量子计算新时代即将到来
  • 做那种事免费网站WordPress网站动漫你在
  • ROS 点云配准与姿态估计
  • 活动预告|海天瑞声与您相约 NCMMSC 2025
  • Java入门级教程22——Socket编程
  • 【Linux系统编程】2. Linux基本指令(上)
  • 网站系统介绍如何设置wordpress的内存
  • 毕业设计做网站做不出网站建设手机端pc端分开
  • Git删除本地与远程tag操作指南
  • 爱网站推广优化wordpress第三方登录教程
  • 23种设计模式——享元模式(Flyweight Pattern)