JavaScript7 分钟阅读

如何在 JavaScript 中解码 URL(完整指南)

JavaScript 提供了 decodeURIComponent() 用于解码单个 URI 组件,以及 decodeURI() 用于解码完整的 URI。使用 try-catch 来处理格式错误的 URI。URL 和 URLSearchParams API 为解析 URL 提供了更安全的替代方案。

decodeURIComponent() — 解码 URI 组件

decodeURIComponent() 是 JavaScript 中用于解码 URL 编码字符串的主要函数。它会解码所有百分号编码序列,将其还原为原始字符。当你需要解码查询参数值、路径片段或 URI 中任何单个组件时,请使用该函数。

// 基本解码
console.log(decodeURIComponent('hello%20world'));
// "hello world"

console.log(decodeURIComponent('price%3D10%26qty%3D2'));
// "price=10&qty=2"

// 解码 Unicode 字符
console.log(decodeURIComponent('caf%C3%A9'));
// "cafe"(带重音符号)

console.log(decodeURIComponent('%E4%B8%AD%E6%96%87'));
// 中文字符

// 从 URL 中解码某个查询参数值
const url = 'https://example.com/search?q=C%2B%2B%20%26%20Java';
const params = url.split('?')[1];
const value = params.split('=')[1];
console.log(decodeURIComponent(value));
// "C++ & Java"

decodeURIComponent() 会解码所有百分号编码序列,包括那些表示保留字符的序列,例如 %2F(/)和 %3F(?)。在处理单个 URI 组件时,这是正确的行为,但如果将其应用于完整的 URL,则可能引发问题。

decodeURI() — 解码完整的 URI

decodeURI() 在保留 URI 结构的同时对完整的 URI 进行解码。与 decodeURIComponent() 不同,它不会解码那些表示 URI 保留字符的序列,例如 %2F(/)、%3F(?)、%23(#)和 %26(&)。

// decodeURI 会保留 URI 结构
console.log(decodeURI('https://example.com/my%20page?q=hello%20world'));
// "https://example.com/my page?q=hello world"
// 空格被解码,但 /、? 和 = 被保留

// 与在完整 URL 上使用 decodeURIComponent 进行对比
console.log(decodeURIComponent('https%3A%2F%2Fexample.com%2Fpath'));
// "https://example.com/path" —— 如果整个 URL 都被编码过,则能正确解码

// decodeURI 不会解码保留字符序列
console.log(decodeURI('path%2Fto%2Ffile'));
// "path%2Fto%2Ffile" —— %2F 未被解码,因为 / 是保留字符
console.log(decodeURIComponent('path%2Fto%2Ffile'));
// "path/to/file" —— %2F 被解码了

当你希望在不改变 URL 结构的前提下让其更易于阅读(例如用于显示目的)时,请使用 decodeURI()。对于大多数编程场景,你会希望将 decodeURIComponent() 应用于单个组件。

处理格式错误的 URI

当遇到无效的百分号编码序列时,decodeURI()decodeURIComponent() 都会抛出 URIError。这种情况会在出现孤立的百分号、不完整的序列或无效的 UTF-8 字节序列时发生。在处理用户提供或来自外部的 URL 时,请务必将解码操作包裹在 try-catch 代码块中。

// 以下代码会抛出 URIError: URI malformed
try {
  decodeURIComponent('%');         // 孤立的百分号
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

try {
  decodeURIComponent('%2');        // 不完整的序列
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

// 安全解码函数
function safeDecode(str) {
  try {
    return decodeURIComponent(str);
  } catch (e) {
    console.warn('Failed to decode:', str);
    return str; // 解码失败时返回原始字符串
  }
}

// 在解码前修复格式错误的百分号序列
function fixAndDecode(str) {
  // 将孤立的 % 替换为 %25(编码后的百分号)
  const fixed = str.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
  return decodeURIComponent(fixed);
}

console.log(fixAndDecode('100% complete'));
// "100% complete"

使用 URL API(推荐)

现代的 URLURLSearchParams API 提供了一种更安全、更结构化的方式来解析和解码 URL。它们会自动处理编码和解码,从而降低出错的风险。

// 解析 URL 并访问其各个组件(自动解码)
const url = new URL('https://example.com/path%20here?q=hello%20world&lang=en');

console.log(url.pathname);  // "/path here"(已解码)
console.log(url.search);    // "?q=hello%20world&lang=en"(原始值)

// URLSearchParams 会自动解码参数值
console.log(url.searchParams.get('q'));     // "hello world"
console.log(url.searchParams.get('lang')); // "en"

// 遍历所有参数
for (const [key, value] of url.searchParams) {
  console.log(key, '=', value);
}
// q = hello world
// lang = en

// URLSearchParams 会将 + 处理为空格(表单编码)
const formParams = new URLSearchParams('q=hello+world&lang=en');
console.log(formParams.get('q'));  // "hello world"

// 构建 URL 并自动编码
const newUrl = new URL('https://example.com/search');
newUrl.searchParams.set('q', 'C++ & Java');
newUrl.searchParams.set('page', '1');
console.log(newUrl.toString());
// "https://example.com/search?q=C%2B%2B+%26+Java&page=1"

常见的解码错误

错误 1:使用 decodeURIComponent() 解码完整的 URL。 如果 URL 中包含已编码的保留字符,这样做可能会破坏其结构。查询值中的 %2F 会变成 /,从而可能改变 URL 的含义。

错误 2:重复解码。 如果一个字符串已经被解码过一次,再次解码可能会产生意外结果或错误。例如,字符串 %2520 首先会被解码为 %20,然后再解码为一个空格。如果你只预期一层编码,那么重复解码就会破坏数据。

// 重复解码的问题
const encoded = '%2520'; // 这是编码后的 %20
console.log(decodeURIComponent(encoded));  // "%20"(正确 —— 一层)
console.log(decodeURIComponent(decodeURIComponent(encoded)));  // " "(被重复解码了!)

// 在解码前检查字符串是否需要解码
function needsDecoding(str) {
  return str !== decodeURIComponent(str);
}

错误 3:未处理 + 号。 decodeURIComponent() 不会+ 转换为空格。如果你正在解码 form-urlencoded 数据,需要先将 + 替换为空格,或者使用能自动处理这一点的 URLSearchParams

// decodeURIComponent 不会将 + 解码为空格
console.log(decodeURIComponent('hello+world'));
// "hello+world"(而不是 "hello world"!)

// 修复方法:在解码前替换 +
function decodeFormValue(str) {
  return decodeURIComponent(str.replace(/\+/g, ' '));
}
console.log(decodeFormValue('hello+world'));
// "hello world"

// 或者使用 URLSearchParams(会自动处理 +)
const params = new URLSearchParams('q=hello+world');
console.log(params.get('q'));
// "hello world"

相关文章

试用我们的免费工具