Node URL.canParse 偶尔拒绝国际化域名,new URL 却成功
Node 20、22 中同一非 ASCII 主机名可能在热路径后被 canParse 判为无效。用真实解析边界保留输入语义,并将校验结果与访问权限分开。
同一条国际化域名 URL,服务刚启动时校验通过,运行一段时间却被 URL.canParse() 拒绝;随后 new URL() 仍能解析成功。若主机名包含非 ASCII 字符,检查实际 Node 版本和校验入口。
明确的触发组合是 Node.js 20.20.2、22.23.2,输入如 https://münchen.example/zahlungen。这里的域名只作示例。现有现象与调用进入优化路径有关,不能把某次循环次数当作固定阈值;24.22.1 在相同结构下没有出现该表现,也不等于已建立所有版本的完整受影响表。
对齐输入和 base,避免比较了两种解析
先确认失败与成功使用的是同一个字符串,必要时记录去除敏感内容后的字符特征、调用位置与 Node 版本。若原调用带 base,构造器对照也必须带同一个 base。省略 base 后相对 URL 失败,是正常解析差异。
22.23.2 的源码仍分别保留 UTF-8 转换的一般入口与 one-byte 快速入口。这能说明为何应关注非 ASCII 输入和调用路径,尚不足以把所有错误都归因于某个已确认回移的补丁。
在需要解析的边界直接保留 URL 对象
业务若本来就要解析 URL,可在这个边界用构造器处理失败,而不是先用 canParse 拒绝:
function parseURL(input, base) {
try {
return base === undefined ? new URL(input) : new URL(input, base);
} catch (error) {
if (error instanceof TypeError) return null;
throw error;
}
}
示例返回 URL 或 null。约定输入是字符串;接入现有代码时保留原来的失败分支,不要把 null 当成空地址继续请求。若 API 必须返回布尔值,可由是否得到对象决定,但不要在后面又用 URL.canParse 做第二次否决。
解析成功只表示 URL 语法可被接受。协议、允许的主机、端口、重定向和服务端请求权限仍要由原策略判断。不要为了绕过误判先剥掉域名中的非 ASCII 字符,那会改变目标,也可能让合法地址变成另一个地址。
验收要让相同输入经历热调用
使用原校验路径回归国际化主机名、普通 ASCII 地址、合法相对地址及确实无效的输入,确认成功与失败分支都保持预期。若校验由依赖库代办,检查它实际调用的接口,局部修了自己的辅助函数并不会自动替换库内部行为。
切换 Node 版本前核对正式发行内容与依赖兼容性,再用相同输入完成回归。不能只凭较新大版本一次未出现误判就宣称所有国际化域名问题已修,也不应为 URL 校验关闭整台进程的优化。


