考古|文档 id 带斜杠要写成 %2F,ES 的路径解析只认转义写法

简介: 考古|文档 id 带斜杠要写成 %2F,ES 的路径解析只认转义写法

2.0.0 的 REST 修复里有一条 "Restore support for escaped '/' as part of document id"。起因是 2.0 开发期间为了让 <logstash-{now/D}> 这种写法不被斜杠切开,路径解析改成了先整体解码再切分,结果文档 id 里正确转义的 %2F 也被切开了。修复方式是回退这段特殊逻辑,只支持转义斜杠。本文对照 v2.4.6 源码和两个提交说明,把这个机制讲清楚。

说明

来源和边界先交代:本文的依据出自 v2.4.6 标签下的官方文件(2.0.0.asciidoc、RestController.java、PathTrie.java),文中引用的原文均来自这些文件。我没有启动集群、也没有跑测试,示例取自官方示例;哪些是文档写明、哪些是我的推断,正文里逐处标注。

01-2.0.0

图 1:官方文件 2.0.0.asciidoc(v2.4.6 标签)。来源:GitHub elastic/elasticsearch v2.4.6 2.0.0.asciidoc

依据在哪

这条修复在 2.0.0 的 release notes 里(release-notes/2.0.0.asciidoc 第 77 行),归在 REST 分类下:

Restore support for escaped '/' as part of document id {pull}14216[#14216] (issues: {issue}13665[#13665], {issue}13691[#13691], {issue}14177[#14177])

对应的提交是 52727264ef65(作者 javanna,2015-10-20),本文引用的英文说明都取自这个提交的说明。它回退的是 93ad6969669e(作者 Martijn van Groningen,2015-09-21,标题 Index name expressions should not be broken up)。

02-RestController

图 2:官方文件 RestController.java(v2.4.6 标签)。来源:GitHub elastic/elasticsearch v2.4.6 RestController.java

特殊分支挡住了通用机制,回退比改聪明更对

这个问题本身很小,值得写下来的是它的过程:

  1. 有一个合理需求:日期数学表达式 <logstash-{now/D}> 里带斜杠,直接写进 URL 会被按 / 切成两段;
  2. 有人在解析层加了一个分支:看起来像 <...> 的片段,里面的斜杠不切;
  3. 为了让这个分支生效,解析顺序被改了(具体改法见下一节),正确转义的 %2F 反而失效;
  4. 最终修复是回退,并要求客户端自己正确转义。

第 4 步是取舍所在。发现回归以后,维护者没有把分支改得更聪明,而是把它整个删掉,把责任交回 HTTP 层的统一规则。判断依据写在提交说明里:给出日期数学表达式的唯一正确方式本来就是转义斜杠。

我的判断是:遇到"通用机制在某个场景下不好用",先查是不是用法有误,再决定要不要在通用机制上加 if。这次的代价是为了让一种本该转义的写法省事,把另一种正确写法弄坏了。

03-api-conventions

图 3:官方文件 api-conventions.asciidoc(v2.4.6 标签)。来源:GitHub elastic/elasticsearch v2.4.6 api-conventions.asciidoc

URL 里的斜杠有歧义

文档 id 是 URL 路径的一部分:

PUT my_index/my_type/my-doc-id

如果 id 本身含斜杠(比如路径型 id a/b/c),就和 URL 的分段结构冲突,服务端没法判断 a/b/c 是一个 id 还是三段路径。我的默认做法是 URL 编码,斜杠写成 %2F:

PUT my_index/my_type/a%2Fb%2Fc

服务端解码后得到的 id 就是 a/b/c。

先解码还是先切分

两件事的顺序决定了 %2F 能不能用。先看修复提交的说明,原文如下:

With #13691 we introduced some custom logic to make sure that date math expressions like <logstash-{now/D}> don't get broken up into two where the slash appears in the expression. That said the only correct way to provide such a date math expression as part of the uri would be to properly escape the '/' instead. This fix also introduced a regression, as it would make sure that unescaped '/' are handled only in the case of date math expressions, but it removed support for properly escaped slashes anywhere else.

大意是:为日期数学表达式加的特殊逻辑只处理了"未转义的斜杠出现在表达式里"这一种情况,同时让别处正确转义的斜杠不再被支持。

提交的 diff 能看到具体改动。被回退的 93ad6969669e 在 PathTrie 里做了两件事:

  1. 删掉了 put 方法里对路径参数的逐段解码(params.put(node.namedWildcard(), value) 取代了带 decoder.decode 的写法);
  2. 在 retrieve 里改成 splitPath(decoder.decode(path)),即先把整条路径解码,再用自己写的 splitPath 切分。splitPath 会在遇到 < 之后暂停切分,直到遇到 >。

整条路径先解码,a%2Fb%2Fc 就变成了 a/b/c,再按 / 切成三段,id 里的 %2F 已经没有了。splitPath 只对 <...> 内部放行,其它位置的斜杠照切。这就是提交说明里 "removed support for properly escaped slashes anywhere else" 的实现层原因。这一段是对 diff 的解读,提交说明没有这样展开。

修复后的行为(v2.4.6)

提交说明里的修复方案:

The solution is to keep supporting escaped slashes only and require client libraries to properly escape them.

This commit reverts 93ad696 and makes sure that our REST tests runner supports escaping of path parts

回到 v2.4.6 的源码,顺序是先切分、再逐段解码:

所以原始路径里的 %2F 在切分时不是分隔符,到了参数阶段才解码成 /,id 保持完整。官方的 API 约定文档对日期数学表达式也是这个要求(api-conventions.asciidoc 第 98 行):

NOTE: The / used for date rounding must be url encoded as %2F in any url.

同一页的示例是 curl -XGET 'localhost:9200/<logstash-{now%2Fd-2d}>/_search',斜杠写成 %2F,大括号和尖括号没有编码。

这个提交还改了 REST 测试框架,让它支持路径分段转义。作者在说明里也提到:"I do find uri encoding quite frustrating in java.",原因是路径的每一段都要单独转义。

动手验证一次

假设 id 是 a/b/c,在 2.x 测试集群上试:

# 斜杠编码成 %2F
curl -XPUT 'localhost:9200/my_index/my_type/a%2Fb%2Fc' -d '{"title":"hello"}'

# 按同样的编码读回,确认 _id
curl -s 'localhost:9200/my_index/my_type/a%2Fb%2Fc'

响应里的 _id 应该是 a/b/c。再对比不转义的写法:

curl -XPUT 'localhost:9200/my_index/my_type/a/b/c' -d '{"title":"hello"}'

这一条的路径比文档 API 多出两段。按路由逻辑它多半匹配不到文档写入的处理器,具体返回什么错误本文没有实测,以你的集群为准。可以确定的是,它不会把 id 当成 a/b/c。

日期数学表达式按官方文档的写法,把斜杠编码:

curl -XGET 'localhost:9200/<logstash-{now%2Fd-2d}>/_search'

2.4.6 文档里的舍入单位是小写 d(now/d)。提交说明里写的 {now/D} 是当时讨论用的写法,直接抄进 2.x 请求之前先对照 api-conventions 第 106–110 行的表格。

排查时的第一个检查点

GET 一个明确存在的文档却返回 404,或者报索引不存在,先看 id 里的特殊字符有没有转义。未转义的斜杠会让请求落到另一个路径上,错误信息指向的是"索引或文档不存在",而不是"路径写错了"。从代码路径看这是合理的:路径先按 / 切分再匹配处理器,多出来的段会被当成别的东西。

能不用斜杠做 id,就别用

转义被支持,只解决了"能不能写进去",后续还有几项成本:

  • URL 变长且不可读:a%2Fb%2Fc 在日志、监控里不容易一眼看出是哪条数据;
  • 每个调用点都要转义:写入、GET、DELETE、UPDATE 全部要转义,漏一个就是 404;
  • 不同客户端对保留字符的处理可能不一致,跨语言的服务容易一边对一边错;
  • 请求途经代理或网关时,%2F 是否会被提前解码还原成 /,取决于它们的配置。如果被还原,路径就会在到达 ES 之前被打断。这是需要在你自己的链路上确认的一项,不同代理的默认行为不同,本文没有逐个核对。

路径型标识的替代做法是把它存成普通字段,文档 id 用不含特殊字符的生成值(哈希、UUID 之类),检索时查那个字段,让标识不出现在 URL 路径里。如果必须用路径型 id(比如要兼容既有规则),就把转义封装到客户端的统一入口,不要在每个调用点手写。

实践要点

  • 客户端必须转义路径分段:id 里含 /、?、#、%、空格等字符时,按 URL 编码规则转义,/ 写成 %2F;
  • 日期数学表达式里的斜杠同样要写成 %2F,这是官方文档明确要求的;
  • _bulk 里的 id 写在请求体的 action 行里,不是 URL 路径,不需要为它转义斜杠;
  • 这条修复属于 2.0,"客户端要正确转义"这个要求在后续版本沿用。

写在最后

文档 id 里含 / 必须编码成 %2F,日期数学表达式里的斜杠也一样。回归的实现原因是解析顺序:整条路径先解码再切分,转义就失效了;修复后恢复成先切分、再逐段解码。

这个案例的取舍是:特殊写法的便利,不值得拿通用机制的正确性去换。真的要用路径型 id,要有统一的转义入口;能避开就避开。

升级或换客户端库之后,如果出现个别 id 突然查不到,先拿其中一个手工转义后请求一次,再对照客户端库对路径的编码方式。

参考

本文对照 Elasticsearch v2.4.6 标签的官方文档核对,引文逐字取自原文。撰写时未启动集群、未运行测试。

目录
相关文章
|
云安全 安全 网络安全
|
7天前
|
JavaScript 安全 前端开发
Nodejs Brotli decoder 代码执行漏洞 (CVE-2026-111497)10629
Node.js发布安全更新修复Brotli解码器高危RCE漏洞(CVE-2026-111497),建议受影响版本用户立即升级。
2007 1
|
14天前
|
JSON 程序员 数据格式
最新版Notepad++下载、安装、配置保姆级教程(附官网安装包)
Notepad++ 是一款免费开源、轻量快速的 Windows 文本编辑器,支持多标签、语法高亮、代码折叠与宏录制,体积小、启动快、资源占用低,兼容 x64/x32/ARM 架构,是程序员和文字工作者的高效利器。
3160 0
|
13天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
2270 44
|
10天前
|
Web App开发 人工智能 前端开发
2026 年 AI 浏览器怎么选?Ego Lite、Tabbit、Chrome DevTools MCP 深度对比,开发者到底该用哪个?
2026年AI浏览器选型指南:Ego Lite专注多Agent协同(Mac优先),Tabbit国产首选,原生支持Windows与Codex控制;Chrome DevTools MCP非浏览器而是开发者利器,深度集成Console/Network/Performance调试能力,让AI真正“看懂并验证”前端代码。
1421 3
|
7天前
|
安全 应用服务中间件 Go
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
PHP2Shell 双 RCE 已遭在野利用:WordPress、Drupal 全在射程内,nginx 其实另有洞。拆成因与修复顺序
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
|
8天前
|
开发者 索引
DeepSeekHarness大肥鱼自带插件市场了?加个地址就能用
Open Plugin Market 是一个开放插件索引平台,收录800+优质DSH插件,支持分类浏览、智能推荐与随机发现。无需安装市场,粘贴索引地址即可一键接入,网页版直访 dshopen.org,助力小开发者被看见。(239字)
1711 1
DeepSeekHarness大肥鱼自带插件市场了?加个地址就能用
|
6天前
|
人工智能 Linux API
Magpie, 轻量本地AI网关!免费Token用不完!
Magpie 是开源AI编程模型调度网关,上线一月获1.8万Star。它通过本地高性能网关统一托管多平台API密钥、账号与免费额度,实现跨Agent的模型秒级切换、静默额度轮换与资源复用,显著降低碎片化管理成本。(239字)
Magpie, 轻量本地AI网关!免费Token用不完!
|
6天前
|
安全 程序员 应用服务中间件
PHP 被曝两大致命漏洞,请所有程序员抓紧做这件事!
PHP 官方发布了紧急安全更新,一口气修复了两个严重漏洞。如果网站和服务器用了 PHP 应该怎么处理?漏洞的原理是什么?本文给你讲清楚
698 0