JavaScript中更安全的URL读写
前言
URL
对于我们开发人员来讲,应该是非常熟悉了。在对URL
进行参数拼接时,我们一般都会直接进行字符串拼接或使用模版字符串,因为这样非常方便,但是我们这样其实会在不知不觉中以不安全的方式编写 URL。
比如,我们通常会这样写:
const url = `https://www.baidu.com?model=${model}&locale=${locale}?query.text=${text}`;
这样确实写起来非常方便,但你可能会在不知不觉中会你的程序带来一些问题。(如上代码就是一段有问题的代码)
常见问题
不正确的分隔符
这种错误可能在新手身上比较常见,但即使是经验老道的程序员也不可能绝对避免这个错误。造成这个错误的罪魁祸首绝大多数是在修改或移动代码之后。例如,你有一个结构正确的 URL,然后将一个片段从一个片段复制到另一个片段,然后错过了参数分隔符的错误排序。
忘记编码
许多时候我们URL
上的参数是需要进行编码的,因为URL
参数可以是任意类型的文本,包括空格和特殊字符,这会给我们带来一些无法预料的问题。
所以为了避免这种情况,我们往往会这样写:
const url = `https://www.baidu.com
?model=${encodeURIComponent(model)}&locale=${encodeURIComponent(
locale
)}&query.text=${encodeURIComponent(text)}`;
但这样的写法给人的感觉是非常的冗余且不雅观 😓
意外的空白字符
有时候我们为了将一个长 URL
分成多行,我们会不小心在 URL 中包含了换行符和额外的空格,这将导致无法按预期进行抓取。
所以为了正确分解URL
字符串,我们通常会这样写:
const url =
`https://www.baidu.com` +
`?model=${encodeURIComponent(model)}&locale=${encodeURIComponent(
locale
)}&query.text=${encodeURIComponent(text)}`;
但这样是我们的代码变得更加混乱以及难以阅读。
难道就没有一种既安全又优雅的方法来编写URL
吗?🤔
URL 构造函数
既优雅又安全的方法就是使用URL
构造函数
URL()
构造函数返回一个新创建的URL
对象,表示由一组参数定义的 URL。如果给定的基本 URL 或生成的 URL 不是有效的 URL 链接,则会抛出一个
TypeError
。
语法
const url = new URL(url [, base])
参数
url
是一个表示绝对或相对 URL 的DOMString
。如果url
是相对 URL,则会将base
用作基准 URL。如果url
是绝对 URL,则无论参数base
是否存在,都将被忽略base
可选 是一个表示基准 URL 的DOMString
,在 url 是相对 URL 时,它才会起效。如果未指定,则默认为''
解决
所以上面的URL
我们就可以这样来写:
const url = new URL("https://www.baidu.com");
url.searchParams.set("model", model);
url.searchParams.set("locale", locale);
url.searchParams.set("text", text);
这样写就可以为我们解决这些问题:
- 分隔符总是正确的(
?
对于第一个参数,以及之后的参数) - 所有参数都自动编码
- 长 URL 跨多行时没有额外空白字符的风险
修改 URL
对于我们在不知道当前参数状态下的情况,它也非常适用。
比如:
url += (url.includes("?") ? "&" : "?") + "foo=bar";
使用URL
构造函数我们可以这样写:
// url是一个URL构造函数
url.searchParams.set("foo", "bar");
// 或者是一个字符串
const structuredUrl = new URL(url);
structuredUrl.searchParams.set("foo", "bar");
url = structuredUrl.toString();
读取 URL
现在,我如果想在没有库的情况下从当前 URL 读取查询参数这个由来已久的问题也得到了解决。
const pageParam = new URL(location.href).searchParams.get("page");
const url = new URL(location.href);
const currentPage = Number(url.searchParams.get("page"));
url.searchParams.set("page", String(currentPage + 1));
location.href = url.toString();
这不仅限于浏览器,它也可以在 Node.js 中使用
const http = require("http");
const server = http.createServer((req, res) => {
const url = new URL(req.url, `https://${req.headers.host}`);
});
URL 属性
URL 实例支持您已经在浏览器中使用的所有属性,例如 onwindow.location
或 anchor 元素,所有这些我们都可以读写:
const url = new URL("https://www.baidu.com/a?page=1");
url.protocol; // https:
url.host; // www.baidu.com
url.pathname; // /a
url.search; // ?page=1
url.href; // https://www.baidu.com/a?page=1
url.origin; // https://www.baidu.com
url.searchParams.get("page"); // 1
常用 URLSearchParams 方法
该URLSearchParams
对象可在URL
实例上访问,url.searchParams
支持许多方便的方法:
searchParams.has(name)
检查搜索参数是否包含给定名称
url.searchParams.has("page"); // true
searchParams.get(name)
获取给定参数的值
url.searchParams.get("page"); // '1'
searchParams.getAll(name)
获取为参数提供的所有值。如果你允许同名的多个值,这很方便,例如
&page=1&page=2
url.searchParams.getAll("page"); // ['1']
searchParams.set(name, value)
设置参数的值
url.searchParams.set("page", "1");
searchParams.append(name, value)
附加一个参数——如果你可能多次支持同一个参数,这很有用,比如
&page=1&page=2
url.searchParams.append("page", "2");
searchParams.delete(name)
从 URL 中完全删除一个参数
url.searchParams.delete("page");
兼容性
new URL
基本支持所有现代浏览器(除了 IE),以及 Node.js。