监听 RabbitMQ 延时交换机的消息数、OpenFeign 路径参数传入斜杠无法正确转义
背景
【MQ】一套为海量消息和高并发热点消息,提供高可用精准延时服务的解决方案
我现在有一个需求,就是监听 RabbitMQ 一个延时交换机的消息数,而 RabbitTemplate 是不存在对应的方法来获取的。
而我们在 RabbitMQ 的控制台却可以发现延时交换机的消息数,所以其开放的 http-api 里存在我们需要的数据,通过抓包可得:
而我们查看这个包,构造请求(抓包+分析的技巧这里不做介绍)
当然你完全可以去看 RabbitMQ 的 http-api 开放文档,但是我觉得有点多,还不如直接抓包
URL:
http://rabbithost:15672/api/exchanges/{virtualHost}/{exchange}?msg_rates_age=60&msg_rates_incr=5
Method:
GET
Header:
- Authorization:
"Basic " + EncryptUtil.encodeBase64(String.format("%s:%s", rabbitMQConfig.getUsername(), rabbitMQConfig.getPassword()));
很快我们就能写一个 OpenFeign 客户端:
@FeignClient(name = "rabbitmq-service", url = "${okr.mq.http-api}")
public interface RabbitMQHttpFeignClient {@GetMapping("/exchanges/{virtualHost}/{exchange}?msg_rates_age=60&msg_rates_incr=5")DelayExchangeVO getMessagesDelayed(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorization,@PathVariable("virtualHost") String virtualHost,@PathVariable("exchange") String exchange);}
但是你会发现,virtualHost 是带 /
的,但是最终的 url 并没有转义,导致路由出错报了 404
- 400 是参数未通过验证、401 未通过身份认证、403 无权限
先说结论!!!
配置一个 Contract (协议,约定),并设置 decodeSlash 为 false !
@Component
public class OpenFeignConfig {@Beanpublic Contract notdecodeSlashContract(){// 无自定义处理器、默认的 ConversionService、取消 %2F -> / 的解码return new SpringMvcContract(Collections.emptyList(), new DefaultConversionService(), Boolean.FALSE);}}
decodeSlash,直译就是“斜杠解码”
encode: /
→ %2F
decode: %2F
→ /
而我们就是阻止 %2F
→ /
,那我们为什么要阻止呢?
问题分析
首先我们可能会想,它是如何转义的,是传入的时候转义,还是最终一起转义:
如果是最终一起转义,那 /
必然不能被转义,否则那些路由都会失效,所以如果是最终转义,无法满足我们的需求:
这里写了个简单的方法,方便理解
public static <P> String buildUrl(String baseUrl, Map<String, List<String>> queryParams, Map<String, P> pathParams) {queryParams = Optional.ofNullable(queryParams).orElseGet(Map::of);pathParams = Optional.ofNullable(pathParams).orElseGet(Map::of);return UriComponentsBuilder.fromHttpUrl(baseUrl).queryParams(new LinkedMultiValueMap<>(queryParams)).buildAndExpand(pathParams).encode() // 开启译码模式.toUriString();
}
如果在传入的时候转义,才能实现我们的效果:
public static <P> String buildUrl(String baseUrl, Map<String, List<String>> queryParams, Map<String, P> pathParams) {queryParams = Optional.ofNullable(queryParams).orElseGet(Map::of);pathParams = Optional.ofNullable(pathParams).orElseGet(Map::of);return UriComponentsBuilder.fromHttpUrl(baseUrl).encode() // 开启译码模式,这里之后路径参数,/ 也会被转义为 %2F!.queryParams(new LinkedMultiValueMap<>(queryParams)).buildAndExpand(pathParams).toUriString();
}
那 OpenFeign 是哪种呢?如果我们没看源码,我们可能没法判断,但我们可以知道,OpenFeign 在解析路径参数的时候,用的是 PathVariableParameterProcessor
参考文章:文章
通过自定义注解 + 自定义处理器的方式,处理请求,我们通过:
data.indexToExpander().put(context.getParameterIndex(), o -> URLEncoder.encode(String.valueOf(o), Charset.defaultCharset());
我们给 {name} 对应的 index 提供了一个解析器,但是貌似没啥用,如果进行双重编码,导致 %
也也被转义了,但如果只是一重编码,最终 /
还是以 /
的形式出现
这一度让我觉得是玄学!
但我对比了 PathVariableParameterProcessor 类的实现,发现其并没有专门对字符串进行编码,所以我猜测底层是定然编码了的,所以我进行了调试,一步步找到了关键代码:
你会发现,如果传入 /
会被转义成 %2F
也就是说,传入时确实已经编码了,你甚至可以实现传入 %2F
但并设置其已编码,所以不会再次编码,等等无论如何各种方式让字符串为 %2F
但是这里有一个属性 encodeSlash,如果为 false,则将最终结果的 %2F
给重新解码成 /
- 说实话我完全不知道为啥要这样,太放剑了🤣
- 如果是路径参数也是个 uri,也有这样的编程方式,但是我觉得很不规范
这也是我不熟悉 SpringMvcContract 导致的啦,不知道还有这么一个参数 decodeSlash
new SpringMvcContract(Collections.emptyList(), new DefaultConversionService(), false)
decodeSlash 设置为 false 后,encodeSlash 就为 true,%2F
就不会重新解码成 /
了,最终也就能达到我们的预期的效果了
相关文章:

监听 RabbitMQ 延时交换机的消息数、OpenFeign 路径参数传入斜杠无法正确转义
背景 【MQ】一套为海量消息和高并发热点消息,提供高可用精准延时服务的解决方案 我现在有一个需求,就是监听 RabbitMQ 一个延时交换机的消息数,而 RabbitTemplate 是不存在对应的方法来获取的。 而我们在 RabbitMQ 的控制台却可以发现延时交…...
希音(Shein)前端开发面试题集锦和参考答案
用 Node 写过什么工具或 npm 包 在实际开发中,使用 Node 编写过多种实用工具和 npm 包。 自动化构建工具 开发了一个简单的自动化构建工具,用于处理前端项目的资源压缩和合并。在前端项目中,为了优化性能,需要对 CSS 和 JavaScript 文件进行压缩,减少文件体积,同时将多个…...

python全栈-Linux基础
python全栈-Linux基础 文章目录 Linux安装/配置网络配置配置Linux远程登录配置虚拟机内部ip配置真机的ip安装XShell和Xftp目录结构用户和用户组用户管理添加用户useradd查看用户id修改用户usermod (选项)(参数)用户密码设置passed (选项)(参数)删除用户userdel [选项] 用户名 用…...

DeepSeek R1助力,腾讯AI代码助手解锁音乐创作新
目录 1. DeepSeekR1模型简介2. 歌词创作流程2.1 准备工作2.2 歌词生成技巧 3. 音乐制作环节3.1 主流AI音乐生成平台 4. 歌曲欣赏5. 总结展望 1. DeepSeekR1模型简介 腾讯AI代码助手最新推出的DeepSeekR1模型不仅在代码生成方面表现出色,其强大的自然语言处理能力也…...
Git安装与配置
安装部分: Windows:下载官网安装包,双击安装,路径选择(注意是否修改),安装选项(是否勾选某些选项,如提到安装时更换编辑器为Nano)。Linux:通过包管…...

【Linux】自定协议和序列化与反序列化
目录 一、序列化与反序列化概念 二、自定协议实现一个加法网络计算器 (一)TCP如何保证接收方的接收到数据是完整性呢? (二)自定义协议 (三)自定义协议的实现 1、基础类 2、序列化与反序列…...
C++基础系列【19】运算符重载
博主介绍:程序喵大人 35- 资深C/C/Rust/Android/iOS客户端开发10年大厂工作经验嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手《C20高级编程》《C23高级编程》等多本书籍著译者更多原创精品文章,首发gzh,见文末👇…...

Python-04BeautifulSoup网络爬虫
2025-03-04-BeautifulSoup网络爬虫 记录BeautifulSoup网络爬虫的核心知识点 文章目录 2025-03-04-BeautifulSoup网络爬虫 [toc]1-参考网址2-学习要点3-核心知识点1. 安装2. 导入必要的库3. 发送 HTTP 请求4. 创建 BeautifulSoup 对象5. 解析 HTML 内容5.1 查找标签5.2 根据属性…...

芯科科技通过全新并发多协议SoC重新定义智能家居连接
MG26系列SoC现已全面供货,为开发人员提供最高性能和人工智能/机器学习功能 致力于以安全、智能无线连接技术,建立更互联世界的全球领导厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB),日前宣…...

python-leetcode-零钱兑换 II
518. 零钱兑换 II - 力扣(LeetCode) 这个问题是 完全背包问题 的一个变体,可以使用 动态规划 来解决。我们定义 dp[i] 为凑成金额 i 的硬币组合数。 思路: 定义 DP 数组 设 dp[i] 表示凑成金额 i 的组合数,初始化 dp[…...

【RabbitMQ】Producer之TTL过期时间 - 基于AMQP 0-9-1
这篇文章和大家分享Producer发布消息时如何设置消息过期时间,包括队列级别和消息级别,还有如何设置队列的过期时间。 消息过期时间 给消息设置TTL,在超过TTL值后,消息就会变成dead message(死信)…...

演示汉字笔顺的工具
视频需要审核,还是gif比较方便,本来就不长。 给小学生辅导汉字笔顺的时候,先是发现“百度汉语”里面有很多类似的笔顺的动画,非常方便。但总是需要上网,而且百度上并不提供针对特定汉字的方便的检索途径,加…...

JVM简单了解
一、JVM概述 目录 一、JVM概述 1.jvm的作用 2.jvm的组成 2.1类加载 2.1.1加载 2.1.2链接 2.1.3初始化 2.1.4类加载器分类 2.1.5双亲委派机制 2.2运行时数据区 2.2.1程序计数器 2.2.2虚拟机栈 2.2.3本地方法栈 2.2.4java堆内存 2.2.5方法区 2.3本地方法库接口 …...

【CSS—前端快速入门】CSS 选择器
CSS 1. CSS介绍 1.1 什么是CSS? CSS(Cascading Style Sheet),层叠样式表,用于控制页面的样式; CSS 能够对网页中元素位置的排版进行像素级精确控制,实现美化页面的效果;能够做到页面的样式和 结构分离; 1…...

【MYSQL数据库异常处理】执行SQL语句报超时异常
MYSQL执行SQL语句异常:The last packet successfully received from the server was 100,107 milliseconds ago. The last packet sent successfully to the server was 100,101 milliseconds ago. 这个错误表明 MySQL 服务器与 JDBC 连接之间的通信超时了。通常由…...

【Day9】make/makeFile如何让项目构建自动化起飞
【Day9】make/makeFile如何让项目构建自动化起飞 使用make命令编写makefile文件依赖管理增量构建makefile注释:#makefile其他语法 make/makefile递归式工作过程 在Linux中,项目自动化构建是指使用一系列工具和脚本来自动执行软件项目的编译、测试、打包和…...

【单片机】嵌入式系统的硬件与软件特性
嵌入式系统的软件结构 嵌入式系统的软件结构一般分为 不带操作系统(Bare Metal) 和 带操作系统(RTOS / Linux) 两种。不同的软件架构适用于不同的应用场景,如 简单控制系统、实时控制系统、物联网、工业自动化等。 嵌…...

C语言学习笔记-初阶(30)深入理解指针2
1. 数组名的理解 在上一个章节我们在使用指针访问数组的内容时,有这样的代码: int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0]; 这里我们使用 &arr[0] 的方式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址&…...

ROM修改进阶教程------修改安卓机型SELinux宽容的几种方式方法 以及第三方系统中如何关闭SELinux宽容
SELinux是一种强制访问控制安全机制,用于增强Linux系统的安全性。在某些情况下,可能需要对 SELinux 进行宽容设置,以满足特定的应用需求。当SELinux处于宽容模式时,系统允许违反安全策略的行为发生,但不会阻止这些行为,通常会在日志中记录这些违规事件。这与强制模式不同…...

亚马逊云科技Marketplace(中国区)上架专业服务产品, “云生态连接器”价值凸显
近日,由西云数据运营的亚马逊云科技Marketplace(中国区)正式支持专业服务产品。此次发布将大幅简化企业对云专业服务的采购流程,实现云软件从规划、部署到支持的全生命周期管理,同时也为合作伙伴提供了更多的销售机会。…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...

ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...
前端工具库lodash与lodash-es区别详解
lodash 和 lodash-es 是同一工具库的两个不同版本,核心功能完全一致,主要区别在于模块化格式和优化方式,适合不同的开发环境。以下是详细对比: 1. 模块化格式 lodash 使用 CommonJS 模块格式(require/module.exports&a…...