尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错

URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错 前端拼 URL 是天天干的事出问题却很少一眼能看出来搜索词里的c到后端变成了c文件名里带个参数就被截断日志里的中文变成%25E5%258C或者干脆一个URIError: URI malformed把整个页面打挂。这些问题基本都出在三个地方用错了编码函数、和空格的两套规矩、编码次数不对。下面七个坑每个都配了能直接跑的代码和实际输出Node 25.8 实测浏览器里行为一致。1. encodeURI 和 encodeURIComponent 不是一回事先看同一个字符串分别过两个函数constuhttps://example.com/search?q前端 后端tagc#top;console.log(encodeURI(u));console.log(encodeURIComponent(u));https://example.com/search?q%E5%89%8D%E7%AB%AF%20%20%E5%90%8E%E7%AB%AFtagc#top https%3A%2F%2Fexample.com%2Fsearch%3Fq%3D%E5%89%8D%E7%AB%AF%20%26%20%E5%90%8E%E7%AB%AF%26tag%3Dc%2B%2B%23top区别一句话encodeURI认为你给的是一整条 URL所以: / ? # 这些有结构意义的字符它一律不动encodeURIComponent认为你给的是URL 里的一小段值除了字母数字和- _ . ! ~ * ( )全部编码。第一行看着像是「编好了」其实已经错了前端 后端里那个没被编码后端会把它解析成q前端和另一个叫后端的参数。2. 给参数值编码要用 encodeURIComponent这是最常见的错误写法constkwc go;console.log(https://example.com/s?qencodeURI(kw));console.log(https://example.com/s?qencodeURIComponent(kw));https://example.com/s?qc%20%20go https://example.com/s?qc%2B%2B%20%26%20go第一种后端拿到的q是c两个加号被当成空格后面的go变成了一个没有值的参数。第二种才是对的。规则很简单拼值用encodeURIComponentencodeURI基本只在「手上有一整条带中文的 URL、只想把它变成合法形式」时用实际项目里我几乎没再用过它。3. 加号和空格两套规矩混用URL 里空格有两种写法%20和。表示空格这件事只在application/x-www-form-urlencoded格式里成立也就是表单提交和查询字符串的传统写法。encodeURIComponent和decodeURIComponent不认这套规矩URLSearchParams认console.log(newURLSearchParams({q:a bc}).toString());console.log(encodeURIComponent(a bc));console.log(newURLSearchParams(qab%2Bc).get(q));console.log(decodeURIComponent(ab%2Bc));qab%2Bc a%20b%2Bc a bc abc最后一行就是坑一个用表单规则编出来的串拿decodeURIComponent去解空格变回来的是跟真正的加号再也分不开了。我自己调试时常用福兮的 URL 编解码工具它底层就是encodeURIComponent/decodeURIComponent这一对所以正好能演示这个坑——解码框里填ab%2Bc%20%E5%8C%97%E4%BA%AC出来的是abc 北京这不算工具的 bug它严格按标准函数来但你要清楚手上这个串是哪套规矩编的。如果是从表单或者别人系统的回调里拿到的先把换成空格再解或者直接交给URLSearchParams。4. 编了两次%25 是信号constonceencodeURIComponent(北京);console.log(once,encodeURIComponent(once));console.log(decodeURIComponent(encodeURIComponent(once)));%E5%8C%97%E4%BA%AC %25E5%258C%2597%25E4%25BA%25AC %E5%8C%97%E4%BA%AC%本身编码后是%25。所以看到%25后面跟着两位十六进制基本就是编了两次。解一次只能回到第一层。常见的来源前端编了一次请求库axios 的params、URL对象的searchParams又编了一次。原则是谁拼字符串谁编码交给库的参数就别自己先编。5. URIError: URI malformeddecodeURIComponent遇到不合法的序列会直接抛异常try{decodeURIComponent(100%);}catch(e){console.log(e.name,e.message);}try{decodeURIComponent(%E4%B8);}catch(e){console.log(e.name,e.message);}URIError URI malformed URIError URI malformed第一个是用户在搜索框里输了「100%」前端没编码就拼进 URL回来解的时候炸了第二个是一个中文字符的 UTF-8 字节被截断了一半。从地址栏、外部回调、日志里拿到的串解码一定要包一层constsafeDecodes{try{returndecodeURIComponent(s.replace(/\/g, ));}catch{returns;}};console.log(safeDecode(100%),|,safeDecode(ab%20c));100% | a b c注意那个replace只适合你确定来源是表单规则的情况否则会把真正的加号也换掉按场景取舍。6. 截断字符串之后再编码emoji 会把它炸掉consts标题表情;constcuts.slice(0,3);console.log(JSON.stringify(cut),cut.length);try{encodeURIComponent(cut);}catch(e){console.log(e.name,e.message);}console.log(encodeURIComponent([...s].slice(0,3).join()));标题\ud83d 3 URIError URI malformed %E6%A0%87%E9%A2%98%F0%9F%98%80JS 字符串按 UTF-16 存一个 emoji 占两个单元。slice(0, 3)正好切在 emoji 中间剩下半个代理对encodeURIComponent不是解码它在编码的时候就抛了。做分享链接、标题截断的时候特别容易遇到后台截了个标题塞进 URL带 emoji 的那几条就报错。按字符截用[...s]或者Intl.Segmenter。7. URL 对象改一个参数别的参数的空格变了URL和URLSearchParams是现在拼 URL 的推荐方式但有一个细节consturlnewURL(https://example.com/a b/文件.pdf?x1 2);console.log(url.href);url.searchParams.set(name,张三李四);console.log(url.href);console.log(url.searchParams.get(name));https://example.com/a%20b/%E6%96%87%E4%BB%B6.pdf?x1%202 https://example.com/a%20b/%E6%96%87%E4%BB%B6.pdf?x12name%E5%BC%A0%E4%B8%89%26%E6%9D%8E%E5%9B%9B张三李四只是set了一个name原来的x1%202变成了x12。因为一碰searchParams整个查询串就按表单规则重新序列化。对标准后端没区别但如果对面是签名校验按原始查询串算 HMAC 的那种签名就对不上了。遇到要签名的接口查询串自己按对方文档拼别让URL对象重排。小结我现在的习惯拼参数值URLSearchParams或encodeURIComponent不用encodeURI。解外来的串包try/catch先想清楚是不是表单规则算不算空格。看到%25查哪里多编了一次。截断字符串按字符截别按 UTF-16 单元截。有签名的接口自己拼查询串别交给URL对象。局限上面这些都是浏览器和 Node 里 JS 的行为。后端各语言的默认规则不完全一样比如有的框架解析查询串时会把当空格有的不会Java 的URLEncoder.encode默认把空格编成和encodeURIComponent的%20不一样。前后端对不上时两边各自打一下原始字符串最省事。在线快速对一下编码结果可以用 forxi.cn 上的 URL 编解码它是纯浏览器本地处理的但记住它解码时不会把当空格。
返回列表