网页链接制作生成
网页链接制作生成
昨天帮同事排查一个线上问题,折腾了半小时才发现是链接链接里多了一个空格。https://example.com/api?制作q=hello world 这种写法扔进浏览器,空格会被转义成 %20,生成但有些后端框架解析时直接挂掉。网页链接这东西,链接看着简单,制作坑是真不少。
网页链接(URL)本质上就是资源的地址。你把它拆开看,结构固定得有点无聊:协议://主机:端口/路径?查询#片段。协议常见的是 http、https,现在 mailto: 或者 tel: 也归这一类。主机可以是域名,也可以是IP。端口不写就默认,http 是80,https 是443。路径就是服务器上的文件位置或者路由。查询字符串以 ? 开头,里面是键值对,用 & 分隔。片段以 # 开头,只在浏览器端起作用,不会发给服务器。
生成一个链接最直接的办法就是手写。但手写容易出错,比如中文、空格、特殊符号。我见过有人把 & 直接写在查询参数里,结果后端只收到了前半截。正确做法是对参数值做 encodeURIComponent。举个例子:
// 错误示范 const bad = 'https://api.com/search?q=hello world&lang=zh'; // 正确做法 const query = encodeURIComponent('hello world'); const good = `https://api.com/search?q=${ query}&lang=zh`; // https://api.com/search?q=hello%20world&lang=zh 如果你在浏览器地址栏里手动敲链接,现代浏览器会帮你转义一部分,但别指望它全能。比如 # 如果不转义,后面的内容就全变成片段了。
trim() 加正则清洗才解决。 用JavaScript的 URL 对象来构造链接会省心很多。它自动处理编码,还能改参数。比如:
const url = new URL('https://example.com/path'); url.searchParams.set('name', '张三 & 李四'); url.searchParams.set('page', '2'); console.log(url.toString()); // https://example.com/path?name=%E5%BC%A0%E4%B8%89+%26+%E6%9D%8E%E5%9B%9B&page=2 注意 + 和 %20 的区别。查询字符串里 + 代表空格,但路径里 + 就是加号本身。所以别乱用。我一般统一用 encodeURIComponent,它把空格转成 %20,更保险。
后端生成链接也一样。Python里用 urllib.parse.urlencode,Node里用 querystring.stringify 或者 URLSearchParams。别自己拼字符串,尤其是参数值来自用户输入的时候。XSS和注入有时候就是从链接拼接开始的。
还有一种场景是短链接生成。你肯定见过 t.cn/xxx 或者 bit.ly/xxx。原理不复杂:存一个映射表,把长链接的哈希或者自增ID转成短码。访问短链时,服务器查表然后302跳转。自己实现的话,可以用Redis做缓存,MySQL存映射。短码最好用62进制(a-z, A-Z, 0-9),6位就能表示568亿种组合。但要注意碰撞,可以用发号器或者预生成。
// 一个极简的短码生成思路(Node.js) const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; function encodeId(id) { let short = ''; while (id > 0) { short = BASE62[id % 62] + short; id = Math.floor(id / 62); } return short || '0'; } console.log(encodeId(123456)); // 'W7E' 不过短链接有个麻烦:如果原链接里带片段(#),跳转后片段会丢失。因为片段不会发给服务器,服务器只能拿到 ? 之前的部分。所以做短链时最好把片段也编码进查询参数,或者干脆不支持。
移动端还有个常见问题:Universal Link 和 App Link。iOS要求你把 apple-app-site-association 文件放在域名根目录,Android要 assetlinks.json。配置错了,链接在微信里点开就是白屏。我调试这个的时候,用 curl -I 看响应头,发现 Content-Type 必须是 application/json,不能是 text/plain。就这点细节,卡了一下午。
最后说个偷懒但有效的方法:用现成的库。前端有 query-string、url-parse,后端有 url 模块。别重复造轮子,除非你想练手。链接生成的核心就两点:正确编码,别丢参数。剩下的就是经验了。
写完这篇文章时,我又去检查了一遍项目里的链接拼接代码,果然发现两处没做编码。改完顺手加了个lint规则。你看,坑都是自己踩出来的。









