Python自动化测试用例:如何优雅的完成Json格式数据断言
目录
前言
直接使用
优化
封装
小结
进阶
总结
资料获取方法
前言
记录Json断言在工作中的应用进阶。
直接使用
很早以前写过一篇博客,记录当时获取一个多级json中指定key的数据:
#! /usr/bin/python
# coding:utf-8
"""
@author:Bingo.he
@file: get_target_value.py
@time: 2017/12/22
"""
def get_target_value(key, dic, tmp_list):""":param key: 目标key值:param dic: JSON数据:param tmp_list: 用于存储获取的数据:return: list"""if not isinstance(dic, dict) or not isinstance(tmp_list, list): # 对传入数据进行格式校验return 'argv[1] not an dict or argv[-1] not an list 'if key in dic.keys():tmp_list.append(dic[key]) # 传入数据存在则存入tmp_listfor value in dic.values(): # 传入数据不符合则对其value值进行遍历if isinstance(value, dict):get_target_value(key, value, tmp_list) # 传入数据的value值是字典,则直接调用自身elif isinstance(value, (list, tuple)):_get_value(key, value, tmp_list) # 传入数据的value值是列表或者元组,则调用_get_valuereturn tmp_listdef _get_value(key, val, tmp_list):for val_ in val:if isinstance(val_, dict): get_target_value(key, val_, tmp_list) # 传入数据的value值是字典,则调用get_target_valueelif isinstance(val_, (list, tuple)):_get_value(key, val_, tmp_list) # 传入数据的value值是列表或者元组,则调用自身
优化
后来在写用例生成过程中,发现重复的断言描述信息较多,大多数数据返回其实都是标准的json,所以将整个返回的json放到断言数据中:
continue_run_flag = Truedef assert_json(base, juge, contain=(), reg=()):# 返回值,不符合预期时会设置为Falseflag = Truefor key, value in base.items():# 不进行断言的数据不进一步处理if key not in juge:continueif key in COMMON_RE_CHECK:if not re.match(COMMON_RE_CHECK[key], base[key]):flag = Falselogger.error("字段[{}]使用通用的正则匹配,不符合预期:预期正则匹配【{}】== 【{}】".format(key, COMMON_RE_CHECK[key], juge[key]))else:logger.warning("字段【{}】使用通用字段的正则匹配, 符合预期".format(key))continueif key in contain:if str(value) not in juge[key]:flag = Falselogger.error("字段[{}]不符合预期:预期【{}】包含 【{}】".format(key, juge[key], value))continuelogger.info("字段[{}]符合预期:预期[{}] 包含 [{}]".format(key, juge[key], value))continueif key in reg:if not re.match(juge[key], value):flag = Falselogger.error("字段[{}]不符合预期:预期正则匹配【{}】== 【{}】".format(key, value, juge[key]))continuelogger.info("字段[{}]断言成功:预期[{}]== 实际[{}]".format(key, value, juge[key]))continueif isinstance(value, str) or isinstance(value, int):if juge[key] != value:logger.error("字段[{}]不符合预期:预期【{}】!= 实际【{}】".format(key, value, juge[key]))flag = Falsecontinueelif isinstance(value, dict):assert_json(value, juge[key], contain=contain, reg=reg)elif isinstance(value, list):for i, v in enumerate(value):if isinstance(value, str) or isinstance(value, int):if v != juge[key][i]:logger.error("字段[{}]不符合预期:预期【{}】!= 实际【{}】".format(key, value, juge[key]))else:logger.info("字段[{}]断言成功:预期[{}]== 实际[{}]".format(key, value, juge[key]))elif isinstance(value, dict):assert_json(value[i], juge[key][i], contain=contain, reg=reg)else:assert_json(value[i], juge[key][i], contain=contain, reg=reg)else:passlogger.info("字段[{}]符合预期: 预期[{}]== 实际[{}]".format(key, value, juge[key]))# 失败是否继续执行,默认为TRUEif not continue_run_flag:assert flagreturn flag
调用:
rsp = requests.get('http://localhost:8800/get', params={"name": "bingo", "age": 18}).json()
assert_json(rsp, {"args": {"name": "bingo"},"headers": {"Accept": "*/*","Accept-Encoding": "gzip, deflate","Cache-Control": "max-age=259200","Host": "httpbin.org","User-Agent": "python-requests/2.27.1","X-Amzn-Trace-Id": r"Root=\d-\w{8}-\w{24}"},"req_param": [{"name": "bingo","age": "18"}],"origin": r"","url": "http://httpbin.org/get?name=bingo"
},
contain=(), reg=("X-Amzn-Trace-Id", "origin",))
日志效果:
2022-05-05 14:25:49.967 | INFO | __main__:assert_json:173 - 字段[name]符合预期: 预期[bingo]== 实际[bingo]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:173 - 字段[args]符合预期: 预期[{'name': 'bingo'}]== 实际[{'name': 'bingo'}]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:173 - 字段[Accept]符合预期: 预期[*/*]== 实际[*/*]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:173 - 字段[Accept-Encoding]符合预期: 预期[gzip, deflate]== 实际[gzip, deflate]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:173 - 字段[Cache-Control]符合预期: 预期[max-age=259200]== 实际[max-age=259200]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:173 - 字段[Host]符合预期: 预期[httpbin.org]== 实际[httpbin.org]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:173 - 字段[User-Agent]符合预期: 预期[python-requests/2.27.1]== 实际[python-requests/2.27.1]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:149 - 字段[X-Amzn-Trace-Id]断言成功:预期[Root=1-62734553-430db0707e1a3656043cd165]== 实际[Root=\d-\w{8}-\w{24}]
2022-05-05 14:25:49.968 | INFO | __main__:assert_json:173 - 字段[headers]符合预期: 预期[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-62734553-430db0707e1a3656043cd165'}]== 实际[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=\\d-\\w{8}-\\w{24}'}]
2022-05-05 14:25:49.969 | WARNING | __main__:assert_json:133 - 字段【origin】使用通用字段的正则匹配, 符合预期
2022-05-05 14:25:49.969 | INFO | __main__:assert_json:173 - 字段[age]符合预期: 预期[18]== 实际[18]
2022-05-05 14:25:49.969 | INFO | __main__:assert_json:173 - 字段[name]符合预期: 预期[bingo]== 实际[bingo]
2022-05-05 14:25:49.969 | INFO | __main__:assert_json:173 - 字段[req_param]符合预期: 预期[[{'age': '18', 'name': 'bingo'}]]== 实际[[{'name': 'bingo', 'age': '18'}]]
2022-05-05 14:25:49.969 | INFO | __main__:assert_json:173 - 字段[url]符合预期: 预期[http://httpbin.org/get?name=bingo]== 实际[http://httpbin.org/get?name=bingo]
封装
将方法简单封装到调用类中:
class HttpBin:def __init__(self):self.continue_run_flag = True # 失败是否继续执行self.base_url = 'http://localhost:8800'self.base_param = {"local_class": self.__class__.__name__}def get(self, param):path = "/get"param.update(self.base_param)self.rsp = requests.get(self.base_url + path, params=param)self.ans = self.rsp.json()logger.info(json.dumps(self.rsp.json(), indent=4))return selfdef set(self, param):path = "/set"param.update(self.base_param)self.rsp = requests.get(self.base_url + path, params=param)self.ans = self.rsp.json()logger.info(json.dumps(self.rsp.json(), indent=4))return selfdef assert_statusCode(self, result_code):""":param result_code: 包含关系断言:return: bool <= self.rsp.resultinfo"""# 返回值,不符合预期时会设置为Falseflag = Trueif int(result_code) != self.rsp.status_code:logger.error(f"返回状态码[result_code]不符合预期:预期【{result_code}】!= 实际【{self.rsp.status_code}】")flag = Falseelse:logger.info(f"返回状态码[result_code]符合预期:预期【{result_code}】!= 实际【{self.rsp.status_code}】")if not self.continue_run_flag:assert flagreturn selfdef assert_json_body(self, base, juge, contain=(), reg=()):...
用例调用
# 如果仅仅断言状态码
HttpBin().get({"name": "bingo", "age": 18}).assert_statusCode(200)# 级连调用多个接口,使用同一个初始化数据
HttpBin().get({"name": "bingo", "age": 18}).assert_statusCode(200).\set({"name": "he", "age": 18}).assert_statusCode(200).assert_json_body(juge={"args": {"name": "bingo"},"headers": {"Accept": "*/*","Accept-Encoding": "gzip, deflate","Cache-Control": "max-age=259200","Host": "httpbin.org","User-Agent": "python-requests/2.27.1","X-Amzn-Trace-Id": r"Root=\d-\w{8}-\w{24}"},"req_param": [{"name": "he","age": "18"}],"origin": r"","url": "http://httpbin.org/set?name=bingo"}, contain=(), reg=("X-Amzn-Trace-Id", "origin",))
运行效果:
2022-05-05 19:39:36.951 | INFO | __main__:assert_statusCode:53 - 返回状态码[result_code]符合预期:预期【200】!= 实际【200】
2022-05-05 19:39:36.951 | INFO | __main__:assert_json_body:117 - 字段[name]符合预期: 预期[bingo]== 实际[bingo]
2022-05-05 19:39:36.951 | INFO | __main__:assert_json_body:117 - 字段[args]符合预期: 预期[{'name': 'bingo'}]== 实际[{'name': 'bingo'}]
2022-05-05 19:39:36.952 | INFO | __main__:assert_json_body:117 - 字段[Accept]符合预期: 预期[*/*]== 实际[*/*]
2022-05-05 19:39:36.952 | INFO | __main__:assert_json_body:117 - 字段[Accept-Encoding]符合预期: 预期[gzip, deflate]== 实际[gzip, deflate]
2022-05-05 19:39:36.952 | INFO | __main__:assert_json_body:117 - 字段[Cache-Control]符合预期: 预期[max-age=259200]== 实际[max-age=259200]
2022-05-05 19:39:36.952 | INFO | __main__:assert_json_body:117 - 字段[Host]符合预期: 预期[httpbin.org]== 实际[httpbin.org]
2022-05-05 19:39:36.952 | INFO | __main__:assert_json_body:117 - 字段[User-Agent]符合预期: 预期[python-requests/2.27.1]== 实际[python-requests/2.27.1]
2022-05-05 19:39:36.952 | INFO | __main__:assert_json_body:93 - 字段[X-Amzn-Trace-Id]断言成功:预期[Root=1-62734553-430db0707e1a3656043cd165]== 实际[Root=\d-\w{8}-\w{24}]
2022-05-05 19:39:36.952 | INFO | __main__:assert_json_body:117 - 字段[headers]符合预期: 预期[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-62734553-430db0707e1a3656043cd165'}]== 实际[{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=259200', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=\\d-\\w{8}-\\w{24}'}]
2022-05-05 19:39:36.953 | WARNING | __main__:assert_json_body:77 - 字段【origin】使用通用字段的正则匹配, 符合预期
2022-05-05 19:39:36.953 | INFO | __main__:assert_json_body:117 - 字段[age]符合预期: 预期[18]== 实际[18]
2022-05-05 19:39:36.953 | INFO | __main__:assert_json_body:117 - 字段[name]符合预期: 预期[he]== 实际[he]
2022-05-05 19:39:36.953 | INFO | __main__:assert_json_body:117 - 字段[req_param]符合预期: 预期[[{'age': '18', 'local_class': 'HttpBin', 'name': 'he'}]]== 实际[[{'name': 'he', 'age': '18'}]]
2022-05-05 19:39:36.953 | INFO | __main__:assert_json_body:117 - 字段[url]符合预期: 预期[http://httpbin.org/set?name=bingo]== 实际[http://httpbin.org/set?name=bingo]
小结
- 可以作为独立函数使用,也可以和所有的接口一起封装,灵活度、复用度高
- 用例极简且逻辑清晰
- 支持断言失败,用例继续执行(开关控制),方便一次性发现所有的差异
- 极易编写的统一断言(从日志获取轻松获取)
- 用例运行日志,断言数据字段清晰明确
- 可统一化处理新版本修改字段,无需修改每个用例
- 支持正则匹配
- 支持**包含匹配 **
- 支持错误码直接断言
- 集中初始化原始常用数据,不同业务适配简单,重写初始化方法即可
进阶
上面的方法虽然使用日志的方法记录了所有的差异,但是面对大json对比的时候,很难直接标记出具体的差异位置。在做现网引流对比测试的时候就出现了这样的需求,从现网拉取的账户数据可能存在几百个子账户,每个子账户有20多个属性字段,怎么准确标记他们在新旧系统的写操作后不一致的情况成为了一个小卡点。
话不多说,思路:利用列表可变特性和生成器关键字yield特性递归分解json,生成一个固定的数组,最终比较数组中的数据
代码:
def recurse(d, prefix=None, sep='.'):if prefix is None:prefix = []for key, value in d.items():if isinstance(value, dict):yield from recurse(value, prefix + [key])elif isinstance(value, list):for i, v in enumerate(value):if isinstance(v, dict):yield from recurse(v, prefix + [key, f"${i}"])# 兼容 包含数字的类型elif isinstance(v, int) or isinstance(v, str):yield sep.join(prefix + [key, str(value)]) # 会嵌套上valueelse:# print(key)yield sep.join(prefix + [key, str(value)]) # 会嵌套上value
效果:
print(json.dumps(list(recurse({"args": {"name": "bingo"},"headers": {"Accept": "*/*","Accept-Encoding": "gzip, deflate","Cache-Control": "max-age=259200","Host": "httpbin.org","User-Agent": "python-requests/2.27.1","X-Amzn-Trace-Id": r"Root=\d-\w{8}-\w{24}"},"req_param": [{"name": "bingo","age": "18"},{"name": "he","age": "19"},{"name": "detector","age": "20"}],"origin": r"","url": "http://httpbin.org/set?name=bingo"})), indent=4))
# 输出
["args.name.bingo","headers.Accept.*/*","headers.Accept-Encoding.gzip, deflate","headers.Cache-Control.max-age=259200","headers.Host.httpbin.org","headers.User-Agent.python-requests/2.27.1","headers.X-Amzn-Trace-Id.Root=\\d-\\w{8}-\\w{24}","req_param.$0.name.bingo","req_param.$0.age.18","req_param.$1.name.he","req_param.$1.age.19","req_param.$2.name.detector","req_param.$2.age.20","origin.","url.http://httpbin.org/set?name=bingo"
]
总结
项目实践中总会遇到这样那样的需求,每个方法都有适用的场景,直接高效解决问题是第一要务。
- json查找数据
- json数据用例封装对比
- json数据转化
资料获取方法
【留言777】


各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!
三连之后我会在评论区挨个私信发给你们~
相关文章:
Python自动化测试用例:如何优雅的完成Json格式数据断言
目录 前言 直接使用 优化 封装 小结 进阶 总结 资料获取方法 前言 记录Json断言在工作中的应用进阶。 直接使用 很早以前写过一篇博客,记录当时获取一个多级json中指定key的数据: #! /usr/bin/python # coding:utf-8 """ aut…...
阿里云对象存储服务OSS
1、引依赖 <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version> </dependency> <dependency><groupId>javax.xml.bind</groupId><artifa…...
第三节:在WORD为应用主窗口下关闭EXCEL的操作(1)
【分享成果,随喜正能量】夏日里的遗憾,一定都会被秋风温柔化解。吃素不难,难于不肯捨贪口腹之心。若不贪口腹,有何吃素之不便乎。虽吃华素,不吃素日,亦须少吃。以一切物类,皆是贪生怕死…...
mybatis 缓存
很久没有弄mybatis了,以至于今天在使用时忘记了一个很重事情(缓存),导致始终读取不数据库更新之后的最新的数据,后来折腾了小半天才想起缓存,所有小记住一下关闭mybatis的缓存 mybatis.configuration.cach…...
分布式Redis详解
目录 前言安装redis的俩种方法Redis 与 MySQL的区别Redis可以实现那些功能Redis常用的数据类型有序列表的底层是如何实现的?什么是跳跃表 Redis在Spring中的使用Redis 中为什么单线程比多线程快Redis的分布式锁如何实现Redis 分布式锁可能出现的问题Redis保持数据不丢失的方式…...
揭秘程序员和技师的7大共同点,最后一点绷不住了
大家好,这里是程序员晚枫,周末朋友出去放松回来,给我分析了一下程序员和技师的7个相同点,尤其是最后一点让我彻底绷不住了! 我也分享给大家。 1、都有工号。98号技师,380号技师大家都很熟悉了,…...
SQL | 使用函数处理数据
8-使用函数处理数据 8.1-函数 SQL可以用函数来处理数据。函数一般是在数据上执行的,为数据的转换和处理提供了方便。 8.1.1 函数带来的问题 每种DBMS都有特定的函数,只有很少一部分函数,是被所有主要的DBMS等同的支持。 虽然所有的类型的…...
基于Dlib库+SVM+Tensorflow+PyQT5智能面相分析-机器学习算法应用(含全部工程源码)+训练及测试数据集
目录 前言总体设计系统整体结构图系统流程图模型流程 运行环境Python 环境TensorFlow环境界面编程环境 模块实现1. 数据预处理2. 模型构建1)定义模型结构2)交叉验证模型优化 3. 模型训练及保存4. 模型测试1)摄像头调用2)模型导入及…...
【Flutter】【packages】simple_animations 简单的实现动画
package:simple_animations 导入包到项目中去 可以实现简单的动画, 快速实现,不需要自己过多的设置 有多种样式可以实现[ ] 功能: 简单的用例:具体需要详细可以去 pub 链接地址 1. PlayAnimationBuilder PlayAnima…...
python之matplotlib入门初体验:使用Matplotlib进行简单的图形绘制
目录 绘制简单的折线图1.1 修改标签文字和线条粗细1.2 校正图形1.3 使用内置样式1.4 使用scatter()绘制散点图并设置样式1.5 使用scatter()绘制一系列点1.6 python循环自动计算数据1.7 自定义颜色1.8 使用颜色映射1.9 自动保存图表练习题 绘制简单的折线图 绘制一个简单折线图…...
[Linux kernel] [ARM64] boot 流程梳理
一、启动汇编代码部分 0. 链接文件找代码段入口 – _text arch/arm64/kernel/vmlinux.lds.S ENTRY(_text). KIMAGE_VADDR;.head.text : {_text .;HEAD_TEXT}.text : ALIGN(SEGMENT_ALIGN) { /* Real text segment */_stext .; /* Text and read-only data */IRQENTRY_TE…...
重建二叉树
输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树。 注意: 二叉树中每个节点的值都互不相同;输入的前序遍历和中序遍历一定合法; 数据范围 树中节点数量范围 [0,100] 。 样例 给定: 前序遍历是:[3, 9, 2…...
支付整体架构
5.4 支付的技术架构 架构即未来,只有建立在技术架构设计良好的体系上,支付机构才能有美好的未来。如果支付的技术体系在架构上存在问题,那么就没有办法实现高可用性、高安全性、高效率和水平可扩展性。 总结多年来在海内外支付机构主持和参与…...
百度智能云:千帆大模型平台接入Llama 2等33个大模型,上线103个Prompt模板
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…...
烦人的幻灯片——拓扑排序
烦人的幻灯片 烦人的幻灯片问题描述输入输出格式输入格式输出格式 输入输出样例输入样例:输入样例一:输入样例二: 输出样例:输出样例一:输出样例二: 正确做法拓扑排序 代码 烦人的幻灯片 问题描述 李教授…...
无涯教程-Perl - ord函数
描述 此函数返回EXPR指定的字符的ASCII数值,如果省略则返回$_。例如,ord(A)返回值为65。 语法 以下是此函数的简单语法- ord EXPRord返回值 该函数返回整数。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -wprint("ord() ", ord(G), "\n"…...
Python爬虫:js逆向调式操作及调式中遇到debugger问题
Python爬虫:js逆向调式操作及调式中遇到debugger问题 1. 前言2. js逆向调式操作2.1 DOM事件断点2.2 XHR/提取断点(用于请求接口参数加密处理)2.3 请求返回的数据是加密的2.4 hook定位参数 3. 调式中遇到debugger问题3.1 解决方式(一律不在此处暂停)3.2 问题:点击一律…...
HTML网页制作技巧:打造出色的用户体验
HTML是构建网页的基础语言,掌握一些关键的技巧可以帮助您创建出色的用户体验。本文将介绍一些HTML网页制作的技巧,从布局和样式到交互和可访问性,为您提供有用的指导。无论您是初学者还是有经验的开发者,这些技巧都将对您的网页设…...
探究使用HTTP代理ip后无法访问网站的原因与解决方案
目录 访问网站的原理是什么 1. DNS解析 2. 建立TCP连接 3. 发送HTTP请求: 4. 服务器响应: 5. 浏览器渲染: 6. 页面展示: 使用代理IP后访问不了网站,有哪些方面的原因 1. 代理IP的可用性: 2. 代理…...
SpringBoot 全局异常处理进阶
待总结 参考文章: SpringBoot 全局异常处理进阶:使用 ControllerAdvice 对不同的 Controller 分别捕获异常并处理 SpringBoot 对 controller 层捕获全局异常并处理的方法(ControllerAdvice 和 ExceptionHandler) 注解RestCont…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
