URI.resolve 把最后一段替换掉了:基地址的斜杠有什么含义
配置的基地址是 https://example.invalid/api/v1,代码调用 resolve("orders"),得到的却是 /api/orders。v1没有丢在网络上,它在构造目标URI时已经被替换。
URI.resolve 做相对引用解析,不是把两个字符串首尾接起来。没有末尾斜杠时,基地址路径的最后一段会作为可替换的末段处理;如果基地址代表目录前缀,应把这个语义明确写进地址或构造方法。
下面在 Java 17 中核对几种引用,不发起网络请求。域名使用保留的无效示例域,不需要DNS可用。
import java.net.URI;
public class ResolvePaths {
public static void main(String[] args) {
URI endpoint = URI.create("https://example.invalid/api/v1");
URI directory = URI.create("https://example.invalid/api/v1/");
System.out.println("endpoint=" + endpoint.resolve("orders"));
System.out.println("directory=" + directory.resolve("orders"));
System.out.println("root=" + directory.resolve("/orders"));
System.out.println("authority=" + directory.resolve("//other.invalid/x"));
System.out.println("absolute=" + directory.resolve("https://other.invalid/x"));
System.out.println("parent=" + directory.resolve("../health"));
}
}
保存为 ResolvePaths.java,运行 java ResolvePaths.java:
endpoint=https://example.invalid/api/orders
directory=https://example.invalid/api/v1/orders
root=https://example.invalid/orders
authority=https://other.invalid/x
absolute=https://other.invalid/x
parent=https://example.invalid/api/health
补斜杠只修复一种含义
目录基地址末尾有斜杠,orders 才会接到 v1 后面。但只在基地址后补斜杠,并不能保证任意相对引用都留在这个目录中。
以 /orders 开头的引用具有从主机根开始的路径,基地址的 /api/v1/ 会被替换。以 //other.invalid/x 开头的引用则可以改变authority,仍继承基地址的scheme。完整的 https://other.invalid/x 更直接地指定新目标。把外部输入交给resolve以后只检查结果非空,无法保证仍在原主机或原路径前缀下。
若这个方法只用于应用内固定端点,最好的约束往往就是端点常量,不必接受任意URI引用。若必须接受相对路径,则要明确允许哪些结构,拒绝scheme、authority以及越出协议范围的路径,并在解析后核对最终目标。字符转义、路径规范化和目标授权各有职责,不能由resolve包办。
normalize 不是访问权限检查
样本的 ../health 会移除上一层路径段。这正是相对路径解析需要的行为。若业务期望所有调用都留在 /api/v1/ 下,就必须另行限制这样的输入。
normalize() 可以处理部分点段形式,却没有连接服务器验证路由,更没有证明百分号转义会在所有代理层按同样次数解释。安全敏感客户端应按照最终HTTP库、代理与服务器的一致协议处理地址,并使用允许目标集合。对地址字符串做一次startsWith,容易把主机前缀、路径边界或大小写规则混在一起。
此外,URI不自动为业务选择默认端口、删除追踪参数或重新排列查询。系统若把URI当缓存键,需要单独决定这些形式是否代表同一个请求。解析结果正确,只说明引用结构得到确定,不说明业务身份已经规范化。
排查时保存解析前后的组件
最有效的证据是基地址原始值、相对引用原始值、结果的scheme、authority与raw path。只打印最后一个URL,通常很难回头判断少掉的路径来自配置、引用开头的斜杠,还是某次重写。
这里也要小心日志中的凭据。地址可能含查询令牌或用户信息,不适合整条记录。诊断工具可以保留结构并对值脱敏;脱敏不应改变斜杠与组件分隔符,否则正好把本次需要观察的行为遮住。
对于已有客户端,修改基地址末尾斜杠属于行为变更,应该拿实际端点集合回归。某些调用可能原本就依赖替换最后一段,统一加斜杠会把它们移到另一层目录。先区分基地址是文档地址、目录前缀还是某个固定端点,再决定构造方式,比在结果上多补一次字符串替换更稳。
运行时错误若是404,也不要马上归因服务器路由。先把不含秘密的目标raw path和预期路径并排核对,往往在发请求以前就能看见差异。URI解析是一套确定规则,关键是配置是否表达了调用方真正想要的基准。