encodeURIComponent和decodeURIComponent

385 阅读4分钟

😎 URL编码大作战:当 &/ 神秘失踪后,我如何靠它一招制敌!🚀

嘿,各位同学、各位战友!你们的老朋友,前端菜鸡又来了 😉。

今天不聊高大上的架构,就聊一个我们每个人都可能遇到,而且一旦遇到就可能让你抓狂半天的小问题——URL 编码。

我遇到的问题:我的参数去哪儿了?😱

还记得我刚入行那会儿,接手了一个用户系统。有个需求是:用户在未登录状态下访问了一个需要权限的页面(比如 https://my-app.com/settings/profile),系统应该跳转到登录页,并在用户成功登录后,再把他们送回之前想访问的那个页面。

听起来很简单,对吧?我当时也是这么想的。我的思路是,把用户想访问的原始URL作为查询参数 redirectUrl 拼在登录页的链接后面。

let originalUrl = "/settings/profile?from=notification";
let loginUrl = `https://my-app.com/login?redirectUrl=${originalUrl}`;

// 我期望的 loginUrl: 
// https://my-app.com/login?redirectUrl=/settings/profile?from=notification

然而,灾难发生了。浏览器地址栏里的 URL 变成了: https://my-app.com/login?redirectUrl=/settings/profile?from=notification

看上去没问题?别急!当我的后端(或者前端路由)去解析这个 URL 时,它看到的是:

  1. 一个参数 redirectUrl,值是 /settings/profile
  2. 一个参数 from,值是 notification

我原本想传递的完整路径 "/settings/profile?from=notification" 被无情地肢解了!redirectUrl 的值不完整,导致用户登录后被送到了一个错误的页面。

一脸懵逼

另一个场景更让我头大。我们的内容管理系统需要一个搜索功能,用户搜了一个关键词 "C# & .NET"。我兴高采烈地把 URL 构建好发给后端,结果后端哥们儿在聊天软件上敲我:“兄弟,你传过来的参数怎么只有 'C#' 啊?'& .NET' 被你吃了吗?”

我当时就懵了... 后来查阅了大量资料才恍然大悟,原来我惹到了 URL 世界里真正的“大佬”——特殊保留字符

我是如何搞定这个“捣蛋鬼”的?🦸

URL(统一资源定位符)就像互联网世界的门牌号,它有自己的一套语法规则。其中,一些字符被赋予了特殊的“语法含义”,比如:

  • ?: 分隔URL路径和查询参数的开始
  • &: 分隔多个查询参数
  • =: 连接参数名和参数值
  • /: 定义路径层级
  • #: 指向页面内的锚点

当这些特殊字符出现在你的参数值里时,它们就会引起歧义,破坏 URL 的结构。为了解决这个问题,我们需要对这些特殊字符进行“伪装”,让它们失去语法含义,变成普普通通的文本。

就在我一筹莫展的时候,encodeURIComponent() 像一位超级英雄一样登场了!

MDN解释encodeURIComponent() 函数通过将特定字符的每个实例替换成代表字符的 UTF-8 编码的一个、两个、三个或四个转义序列来编码 URI。与 encodeURI() 相比,此函数会编码更多的字符,包括 URI 语法的一部分。

encodeURIComponent() 就像一个专业的“伪装大师”。它非常严格,会把所有可能引起麻烦的特殊字符(比如 &, ?, /, = 等),统统转换成 % 加上两位十六进制数的格式。它只会放过一小部分“安全”的字符(字母、数字和 - _ . ! ~ * ' ( ))。

场景一:拯救被肢解的 redirectUrl

面对我那个被肢解的重定向链接,我用 encodeURIComponent() 对它进行了“伪装”:

let originalUrl = "/settings/profile?from=notification";
// 使用 encodeURIComponent() 进行伪装!
let encodedRedirectUrl = encodeURIComponent(originalUrl);

console.log(encodedRedirectUrl);
// 输出: %2Fsettings%2Fprofile%3Ffrom%3Dnotification

let loginUrl = `https://my-app.com/login?redirectUrl=${encodedRedirectUrl}`;
console.log(loginUrl);
// ✅ 正确的URL: 
// https://my-app.com/login?redirectUrl=%2Fsettings%2Fprofile%3Ffrom%3Dnotification

看!/ 被伪装成了 %2F? 被伪装成了 %3F= 被伪装成了 %3D。现在,当服务器解析这个 URL 时,它会把 %2Fsettings%2Fprofile... 这一长串看作一个完整的值,皆大欢喜!

场景二:找回失踪的 & .NET

对于那个搜索功能,我也是如法炮制:

let keyword = "C# & .NET";
let encodedKeyword = encodeURIComponent(keyword);

console.log(encodedKeyword);
// 输出: C%23%20%26%20.NET

let searchUrl = `https://my-app.com/search?q=${encodedKeyword}`;
console.log(searchUrl);
// ✅ 正确的URL: https://my-app.com/search?q=C%23%20%26%20.NET

# 被伪装成 %23,空格是 %20&%26。这下,后端老哥收到的 q 参数值就是完完整整的 "C# & .NET" 了。问题解决!

完美搭档:decodeURIComponent() 的“卸妆”时刻 🕵️

光会加密还不行,有“伪装”就得有“卸妆”。当你的应用程序(无论是前端从路由里取值,还是后端从请求里取值)获取到被编码过的参数时,就需要 decodeURIComponent() 来把它恢复成原始的、人类可读的样子。

MDN解释decodeURIComponent() 方法用于解码由 encodeURIComponent 方法或者其他类似方法编码的部分统一资源标识符(URI)。

场景三:还原 redirectUrl

在我的登录成功逻辑里,我需要从 URL 中拿到 redirectUrl 并解码,然后才能跳转:

// 假设从路由中获取到的 redirectUrl 是那一长串编码后的字符串
let encodedUrlFromRoute = "%2Fsettings%2Fprofile%3Ffrom%3Dnotification";

// 用 decodeURIComponent 来“卸妆”
let originalUrl = decodeURIComponent(encodedUrlFromRoute);

console.log(originalUrl);
// ✅ 正确输出: /settings/profile?from=notification

// 接下来就可以安全地跳转了
// router.push(originalUrl);
前辈的 Pro-Tip:拥抱 URLSearchParams

说实话,现在我基本都用 URLSearchParams 这个浏览器内置的神器了。它能自动帮你处理参数的解码,让代码更简洁、更健壮。

// 假设 window.location.search 是 "?redirectUrl=%2Fsettings%2Fprofile%3Ffrom%3Dnotification"
const params = new URLSearchParams(window.location.search);

// .get() 方法会自动帮你解码!
const redirectUrl = params.get('redirectUrl'); 

console.log(redirectUrl);
// ✅ 正确输出: /settings/profile?from=notification

是不是优雅多了?强烈推荐!

我差点踩的坑:encodeURI vs encodeURIComponent 🧐

这里要特别提醒大家一个我当年差点踩进去的坑。JavaScript 还有另一个长得很像的函数叫 encodeURI()。千万别用混了!

你可以把它们想象成两兄弟:

  • encodeURIComponent() (弟弟): 性格非常严格,生怕出问题。它会编码所有它认为有风险的特殊字符,包括 &, =, ?, / 等。它适用于编码URL的一个组成部分(比如查询参数的值、路径的一部分)。
  • encodeURI() (哥哥): 性格比较宽容,它知道有些字符是用来构建URL的,所以不会编码 &, =, ?, / 这些“保留字符”。它适用于编码一个完整的URL,比如你的URL里本身就包含了中文字符,你想对整个URL进行编码时才用它。

一句话原则,记住就不会错:当你要把一个塞进URL的查询参数里时,永远优先使用 encodeURIComponent()!99% 的场景下,它都是你正确的选择。

总结:成为URL编码大师的秘诀 ✨

好了,今天的“踩坑经验分享会”就到这里。回顾一下,成为URL编码大师的秘诀其实很简单:

  1. 识别风险:任何时候,只要你想把一个可能包含特殊字符(&, ?, /, #, 空格等)或中文的字符串放进URL,就要亮起警灯,想起编码!
  2. 选择武器:坚决使用 encodeURIComponent() 来对你的数据进行“伪装”,确保它在URL中安全传输。
  3. 还原真相:在接收端,使用 decodeURIComponent() (或者更推荐的 URLSearchParams)来“卸妆”,获取原始数据。
  4. 牢记区别:只在需要编码整个URL时才考虑 encodeURI(),编码URL的部分时,请坚决使用 encodeURIComponent()

希望这篇分享能帮到大家,下次再遇到 URL 编码问题,你就是那个能一招制敌的大佬了!下次见!👋