网络编程(24)——实现带参数的http-get请求

文章目录

  • 二十四、day24
  • 1. char 转为16进制
  • 2. 16进制转为 char
  • 3. URL 编码函数
  • 4. URL 解码函数
  • 5. 实现 get 请求参数的解析
  • 6. 测试

二十四、day24

我们在前文通过beast实现了http服务器的简单搭建,但是有很多问题我们并没有解决。

在前文中,我们的 get 请求不带任何参数,那如果我们想要实现带参数的 get 请求,我们应该如何做?首先应考虑实现 url 的解析函数,解析 get 请求携带的参数。

1. char 转为16进制

unsigned char ToHex(unsigned char x) {return x > 9 ? x + 55 : x + 48;
}

该函数用于将 4 位无符号整数(取值范围为 0 到 15)转换为十六进制字符(0到9 和 A到F),超过这个范围的值不属于十六进制的规范。

  • 如果 x > 9,说明它是 10 到 15 之间的值,对应十六进制的 AF。此时,x + 55 将其转换为对应的 ASCII 字符。
  • 如果 x <= 9,说明它是 0 到 9 之间的值,对应十六进制的 09。此时,x + 48 将其转换为对应的 ASCII 字符。

举例:

int main() {for (int i = 0; i <= 15; ++i) {std::cout << "ToHex(" << i << ") = " << ToHex(i) << std::endl;}return 0;
}

输出:

ToHex(0) = 0
ToHex(1) = 1
ToHex(2) = 2
ToHex(3) = 3
ToHex(4) = 4
ToHex(5) = 5
ToHex(6) = 6
ToHex(7) = 7
ToHex(8) = 8
ToHex(9) = 9
ToHex(10) = A
ToHex(11) = B
ToHex(12) = C
ToHex(13) = D
ToHex(14) = E
ToHex(15) = F

2. 16进制转为 char

unsigned char FromHex(unsigned char x) {unsigned char y;if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;  // 处理大写字母else if (x >= 'a' && x <= 'z') y = x - 'a' + 10; // 处理小写字母else if (x >= '0' && x <= '9') y = x - '0';      // 处理数字else assert(0); // 如果输入非法,触发断言return y;
}

该函数用于将一个 十六进制字符 转换为其对应的 4 位无符号整数值,支持处理大写字母(A-Z)、小写字母(a-z)和数字(0-9

3. URL 编码函数

至于为什么需要上面两个函数实现十六进制和十进制的互转,得益于下面这个函数。

std::string UrlEncode(const std::string& str) {std::string strTemp = "";size_t length = str.length();for (size_t i = 0; i < length; i++) {// 判断字符是否无需编码(字母、数字、安全字符)if (isalnum((unsigned char)str[i]) ||(str[i] == '-') || (str[i] == '_') || (str[i] == '.') || (str[i] == '~')) {strTemp += str[i];}// 处理空格(替换为 '+')else if (str[i] == ' ') {strTemp += "+";}// 其他字符进行百分比编码(%XX)else {strTemp += '%';strTemp += ToHex((unsigned char)str[i] >> 4);  // 高 4 位strTemp += ToHex((unsigned char)str[i] & 0x0F); // 低 4 位}}return strTemp;
}

该函数对输入的字符串进行 URL 编码(也称为百分比编码),确保字符串可以在 URL 中安全传输。URL 编码会将特殊字符转换为 % 后跟两个十六进制字符的形式。

编码规则

  1. 无需编码的字符
    • 字母(A-Za-z)、数字(0-9)。
    • 安全字符:-_.~(根据 RFC 3986 标准)。
  2. 空格处理
    • 空格被替换为 +(符合 application/x-www-form-urlencoded 格式,常见于表单提交)。
  3. 需要编码的字符
    • 其他所有字符(如 !#$%&= 等)会被转换为 % 后跟两个十六进制字符。
    • 例如,字符 @ 的 ASCII 码是 0x40,会被编码为 %40

举例

假设输入字符串为 "Hello World!@#",编码过程如下:

  1. 'H''e''l''l''o':无需编码。
  2. ' '(空格):替换为 +
  3. 'W''o''r''l''d':无需编码。
  4. '!':ASCII 码为 0x21,编码为 %21
  5. '@':ASCII 码为 0x40,编码为 %40
  6. '#':ASCII 码为 0x23,编码为 %23

最终编码结果:
"Hello+World%21%40%23"

百分比编码

  • 使用 ToHex 函数将字符的高 4 位和低 4 位转换为十六进制字符。
  • 例如,字符 '#'(ASCII 码 0x23)的转换:
    • 高 4 位:0x2'2'ToHex(0x2) = '2')。
    • 低 4 位:0x3'3'ToHex(0x3) = '3')。
    • 结果:%23
  • 同理,汉字的编码过程同样如此

严格遵循 URL 编码标准(RFC 3986)时,空格应编码为 %20,而非 ++application/x-www-form-urlencoded 的约定。若需兼容 RFC 3986,可将 str[i] == ' ' 分支改为 strTemp += "%20"

4. URL 解码函数

std::string UrlDecode(const std::string& str)
{std::string strTemp = "";size_t length = str.length();for (size_t i = 0; i < length; i++){//还原+为空if (str[i] == '+') strTemp += ' ';//遇到%将后面的两个字符从16进制转为char再拼接else if (str[i] == '%'){assert(i + 2 < length);unsigned char high = FromHex((unsigned char)str[++i]);unsigned char low = FromHex((unsigned char)str[++i]);// 将高 4 位和低 4 位组合成一个字节,即将一个十六进制字符转化为十进制strTemp += high * 16 + low;}else strTemp += str[i];}return strTemp;
}

该函数对 URL 编码的字符串进行解码,将其还原为原始字符串。

5. 实现 get 请求参数的解析

参考网络编程(21)——通过beast库快速实现http服务器 | 爱吃土豆的个人博客,我们在 HttpConnection 类中新添加两个私有成员:

std::string _get_url;
std::unordered_map<std::string, std::string> _get_params;

因为 URL 中 GET 请求的参数是通过查询字符串表示的,查询字符串附加在 URL 的末尾,用于向服务器传递键值对形式的参数,格式如下:

?key1=value1&key2=value2&key3=value3
  • ?:表示查询字符串的开始。
  • key=value:参数以键值对的形式表示。
  • &:用于分隔多个键值对。

举例

假设有一个 URL:

https://www.example.com/search?q=hello+world&lang=en%2Fus&page=2
  • 查询字符串?q=hello+world&lang=en%2Fus&page=2
  • 参数
    • q=hello world
    • lang=en/us
    • page=2

并定义函数 PreParseGetParam用于对查询字符串进行编解码:

void HttpConnection::PreParseGetParam() {// 提取 URI  http://localhost:1250/get_test?key1=value1&key2=value2auto uri = _request.target();// 查找查询字符串的开始位置(即 '?' 的位置)  auto query_pos = uri.find('?');if (query_pos == std::string::npos) {_get_url = uri;return;}// 获取 URI 的路径部分(? 前的部分)_get_url = uri.substr(0, query_pos);// 获取查询字符串(? 后的部分)std::string query_string = uri.substr(query_pos + 1);std::string key;std::string value;size_t pos = 0; // 循环中找到每个 & 分隔符的位置while ((pos = query_string.find('&')) != std::string::npos) {auto pair = query_string.substr(0, pos);size_t eq_pos = pair.find('=');if (eq_pos != std::string::npos) {key = UrlDecode(pair.substr(0, eq_pos)); // 假设有 url_decode 函数来处理URL解码  value = UrlDecode(pair.substr(eq_pos + 1));_get_params[key] = value;}query_string.erase(0, pos + 1);}// 处理最后一个参数对(如果没有 & 分隔符)  if (!query_string.empty()) {size_t eq_pos = query_string.find('=');if (eq_pos != std::string::npos) {key = UrlDecode(query_string.substr(0, eq_pos));value = UrlDecode(query_string.substr(eq_pos + 1));_get_params[key] = value;}}
}

然后在函数 process_request 中的 get 部分添加:

void process_request() {
//................case http::verb::get:PreParseGetParam();
//................
}

然后在 create_response 函数中添加如下代码:

void create_response() {
//................else {_response.result(http::status::not_found);_response.set(http::field::content_type, "text/plain");beast::ostream(_response.body()) << "File not found\r\n";}int i = 0;for (auto& elem : _get_params) {i++;beast::ostream(connection->_response.body()) << "param " << i << "key is " << 		elem.first;beast::ostream(connection->_response.body()) << "param " << i << "value is " << 	elem.second << std::endl;}
}

用于显示获取的参数.

6. 测试

浏览器地址栏中输入:localhost:1250/get_test?key1=value1&key2=value2

浏览器显示如下结果:

在这里插入图片描述

不带参数的结果为:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/20496.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习_18 K均值聚类知识点总结

K均值聚类&#xff08;K-means Clustering&#xff09;是一种经典的无监督学习算法&#xff0c;广泛应用于数据分组、模式识别和降维等领域。它通过将数据划分为K个簇&#xff0c;使得簇内相似度高而簇间相似度低。今天&#xff0c;我们就来深入探讨K均值聚类的原理、实现和应用…

LeetCode1287

LeetCode1287 目录 题目描述示例思路分析代码段代码逐行讲解复杂度分析总结的知识点整合总结 题目描述 给定一个非递减的整数数组 arr&#xff0c;其中有一个元素恰好出现超过数组长度的 25%。请你找到并返回这个元素。 示例 示例 1 输入: arr [1, 2, 2, 6, 6, 6, 6, 7,…

恒创科技:如何重新启动 Windows 服务器

重新启动 Windows 服务器对于应用更新、解决问题和维护系统性能至关重要。定期重新启动有助于确保服务器运行最新软件、解决冲突并清除临时文件。本教程将介绍如何使用不同的方法重新启动 Windows 服务器。 注意&#xff1a;重新启动服务器之前保存所有工作&#xff0c;以避免丢…

Django ModelForm使用(初学)

1.目的是根据员工表字段&#xff0c;实现一个新增员工的数据填写页面 2.在views.py文件中按下面的格式写 定义 ModelForm 类&#xff1a;UserModelForm &#xff08;自己命名的类名&#xff09;使用时需要导入包 定义视图函数&#xff1a;user_model_form_add&#xff08;在函…

华为固态电池引发的思索

华为固态电池真牛&#xff01; 超长续航&#xff1a;单次充电即可行驶3000公里 极速充电&#xff1a;五分钟内充满80% 极致安全&#xff1a;不可燃、不漏液 长寿命设计&#xff1a;循环寿命达10000次以上 如上是华为电池展示的优势项&#xff0c;每一条都让我们心动不已。…

美信监控易:运维新时代,守护数据安全

在 2025 年这个科技飞速发展的时代&#xff0c;数据安全已成为各行业关注的焦点。随着云计算、大数据、物联网等技术的不断推进&#xff0c;运维数据的保护面临着新的挑战与要求。美信时代公司的美信监控易运维管理软件&#xff0c;以其卓越的功能、特性和竞争力&#xff0c;为…

个人博客5年回顾

https://huangtao01.github.io/ 五年前&#xff0c;看程序羊的b站视频做的blog&#xff0c;受限于网络&#xff0c;只能单向学习&#xff0c;没有人指导与监督&#xff0c;从来没有想过&#xff0c;有没有什么问题&#xff1f; 一、为什么要做个人博客&#xff1f; 二、我是怎么…

Unity合批处理优化内存序列帧播放动画

Unity合批处理序列帧优化内存 介绍图片导入到Unity中的处理Unity中图片设置处理Unity中图片裁剪 创建序列帧动画总结 介绍 这里是针对Unity序列帧动画的优化内容&#xff0c;将多个图片合批处理然后为了降低Unity的内存占用&#xff0c;但是相对的质量也会稍微降低。可自行进行…

【Docker】容器被停止/删除的方式及命令:全面解析与实践指南

文章目录 引言一、容器的生命周期二、停止容器的命令及方式1. docker stop 命令2. docker kill 命令3. docker pause 和 docker unpause 命令4. docker restart 命令 三、删除容器的命令及方式1. docker rm 命令2. docker container prune 命令3. docker rm 与 docker rmi 的区…

大数据SQL调优专题——Flink执行原理

引入 上一篇我们了解了Spark&#xff0c;相比起MapReduce来说&#xff0c;它确实已经快了超级多了&#xff0c;但是人类的欲望是没有止境的&#xff0c;这也是推动人类进步的动力。 Flink就是为了满足实时响应的场景需求诞生的。 其实在Flink之前&#xff0c;实时处理其实已…

【Cocos TypeScript 零基础 16.1】

目录 FlappyBird背景其他心得_刚体audio部分 FlappyBird 本人没有按照老师的做法去做,大体差不多, 当然老师做的更精细,有些不会的还是参考老师的方法 参考部分 小鸟如何像真实物体一样的重力效果点击如何使小鸟飞翔 省略部分 3. 小鸟多动画(飞机大战其实有做,单纯偷懒) 4. …

CHARMM-GUI EnzyDocker: 一个基于网络的用于酶中多个反应状态的蛋白质 - 配体对接的计算平台

❝ "CHARMM-GUI EnzyDocker for Protein−Ligand Docking of Multiple Reactive States along a Reaction Coordinate in Enzymes"介绍了 CHARMM-GUI EnzyDocker&#xff0c;这是一个基于网络的计算平台&#xff0c;旨在简化和加速 EnzyDock 对接模拟的设置过程&…

腿足机器人之六- 前向运动学

腿足机器人之六- 前向运动学 刚体运动学基础坐标系定义旋转矩阵与欧拉角齐次变换矩阵&#xff08;平移旋转的统一表示&#xff09; 运动链建模串联运动链结构&#xff08;从基座到末端的关节连接&#xff09;标准Denavit-Hartenberg&#xff08;D-H&#xff09;参数法改进D-H参…

uni-app发起网络请求的三种方式

uni.request(OBJECT) 发起网络请求 具体参数可查看官方文档uni-app data:请求的参数; header&#xff1a;设置请求的 header&#xff0c;header 中不能设置 Referer&#xff1b; method&#xff1a;请求方法&#xff1b; timeout&#xff1a;超时时间&#xff0c;单位 ms&a…

【linux】更换ollama的deepseek模型默认安装路径

【linux】更换ollama的deepseek模型默认安装路径 文章目录 【linux】更换ollama的deepseek模型默认安装路径Ollama 默认安装路径及模型存储路径迁移ollama模型到新的路径1.创建新的模型存储目录2.停止ollama3.迁移现有模型4.修改 Ollama 服务配置5.重启ollama6.验证迁移是否成功…

「软件设计模式」装饰者模式(Decorator)

深入解析装饰者模式&#xff1a;动态扩展功能的艺术&#xff08;C实现&#xff09; 一、模式思想与应用场景 1.1 模式定义 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它通过将对象放入包含行为的特殊封装对象中&#xff0c;动态地…

java项目打包成docker镜像步骤

java项目打包成docker镜像步骤 1.使用maven把java文件打包成可执行的jar包2. 打包成Dockerfile3. 把jar包和DockerFile两个文件上传到服务器上。4. 制作镜像5.启动容器 1.使用maven把java文件打包成可执行的jar包 2. 打包成Dockerfile # 先从dockerhub找到对应版本的openjdk的…

后台管理系统-项目初始化

认识vue-admin **核心交付:** 为什么要基于现成架子二次开发 什么是二次开发:基于已有的代码(项目工程,脚手架)开进行新功能的开发 所以看懂已有的框架中的既有代码,变得很重要了 1. 背景知识 后台管理系统是一种最常见的应用模式,不同的管理系统之间有很多相似的地方…

【Scrapy】Scrapy教程6——提取数据

前一小节我们拿到了页面的数据,那页面中那么多内容,我们想要其中的部分内容,该如何获取呢?这就需要对我们下载到的数据进行解析,提取出来想要的数据,这节就讲讲如何提取数据。 引入 我们编辑保存下来的shouye.html文件看下,发现这是什么鬼,全是如下图的代码。 没错…

RabbitMQ 的工作模式

目录 工作模式 Simple&#xff08;简单模式&#xff09; Work Queue&#xff08;工作队列&#xff09; Publish/Subscribe&#xff08;发布/订阅&#xff09; Exchange&#xff08;交换机&#xff09;? Routing&#xff08;路由模式&#xff09; Topics&#xff08;通配…