JavaScript 中更安全的 URL 读写
<h2 id="前言">前言</h2><p><code>URL</code>对于我们开发人员来讲,应该是非常熟悉了。在对<code>URL</code>进行参数拼接时,我们一般都会直接进行字符串拼接或使用模版字符串,因为这样非常方便,但是我们这样其实会在不知不觉中以不安全的方式编写 URL。</p>
<p>比如,我们通常会这样写:</p>
<pre><code class="language-js">const url = `https://www.baidu.com
?model=${model}&locale=${locale}?query.text=${text}`
</code></pre>
<p>这样确实写起来非常方便,但你可能会在不知不觉中会你的程序带来一些问题。(如上代码就是一段有问题的代码)</p>
<p><strong>如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,文章公众号首发,关注 <code>前端南玖</code> 第一时间获取最新文章~</strong></p>
<h2 id="常见问题">常见问题</h2>
<h3 id="不正确的分隔符">不正确的分隔符</h3>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1fead5ff566a4e8b97297505513a89ff~tplv-k3u1fbpfcp-watermark.image?" alt="1fg.png" loading="lazy"></p>
<p>这种错误可能在新手身上比较常见,但即使是经验老道的程序员也不可能绝对避免这个错误。造成这个错误的罪魁祸首绝大多数是在修改或移动代码之后。例如,你有一个结构正确的 URL,然后将一个片段从一个片段复制到另一个片段,然后错过了参数分隔符的错误排序。</p>
<h3 id="忘记编码">忘记编码</h3>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7d6f9ffa24fc48c29926ab356341c222~tplv-k3u1fbpfcp-watermark.image?" alt="2bm.png" loading="lazy"></p>
<p>许多时候我们<code>URL</code>上的参数是需要进行编码的,因为<code>URL</code>参数可以是任意类型的文本,包括空格和特殊字符,这会给我们带来一些无法预料的问题。</p>
<p>所以为了避免这种情况,我们往往会这样写:</p>
<pre><code class="language-js">const url = `https://www.baidu.com
?model=${
encodeURIComponent(model)
}&locale=${
encodeURIComponent(locale)
}&query.text=${
encodeURIComponent(text)
}`
</code></pre>
<p>但这样的写法给人的感觉是非常的冗余且不雅观😓</p>
<h3 id="意外的空白字符">意外的空白字符</h3>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/033a4b66a4f94bc9ab451854a650d703~tplv-k3u1fbpfcp-watermark.image?" alt="3kb.png" loading="lazy"></p>
<p>有时候我们为了将一个长 <code>URL</code> 分成多行,我们会不小心在 URL 中包含了换行符和额外的空格,这将导致无法按预期进行抓取。</p>
<p>所以为了正确分解<code>URL</code>字符串,我们通常会这样写:</p>
<pre><code class="language-js">const url = `https://www.baidu.com`
+ `?model=${
encodeURIComponent(model)
}&locale=${
encodeURIComponent(locale)
}&query.text=${
encodeURIComponent(text)
}`
</code></pre>
<p>但这样是我们的代码变得更加混乱以及难以阅读。</p>
<p>难道就没有一种既安全又优雅的方法来编写<code>URL</code>吗?🤔</p>
<h2 id="url构造函数">URL构造函数</h2>
<p>既优雅又安全的方法就是使用<code>URL</code>构造函数</p>
<blockquote>
<p><strong><code>URL()</code></strong> 构造函数返回一个新创建的 <code>URL</code>对象,表示由一组参数定义的 URL。</p>
<p>如果给定的基本 URL 或生成的 URL 不是有效的 URL 链接,则会抛出一个<code>TypeError</code>。</p>
</blockquote>
<h3 id="语法">语法</h3>
<pre><code class="language-js">const url = new URL(url [, base])
</code></pre>
<p><strong>参数</strong></p>
<ul>
<li>
<p><code>url</code></p>
<p>是一个表示绝对或相对 URL 的 <code>DOMString</code>。如果<code>url</code> 是相对 URL,则会将 <code>base</code> 用作基准 URL。如果 <code>url</code> 是绝对 URL,则无论参数<code>base</code>是否存在,都将被忽略</p>
</li>
<li>
<p><code>base</code> 可选</p>
<p>是一个表示基准 URL 的 <code>DOMString</code>,在 <em>url</em> 是相对 URL 时,它才会起效。如果未指定,则默认为 <code>''</code></p>
</li>
</ul>
<h3 id="解决">解决</h3>
<p>所以上面的<code>URL</code>我们就可以这样来写:</p>
<pre><code class="language-js">const url = new URL('https://www.baidu.com')
url.searchParams.set('model', model)
url.searchParams.set('locale', locale)
url.searchParams.set('text', text)
</code></pre>
<p>这样写就可以为我们解决这些问题:</p>
<ul>
<li>分隔符总是正确的(<code>?</code>对于第一个参数,以及之后的参数)</li>
<li>所有参数都自动编码</li>
<li>长 URL 跨多行时没有额外空白字符的风险</li>
</ul>
<h2 id="修改url">修改URL</h2>
<p>对于我们在不知道当前参数状态下的情况,它也非常适用。</p>
<p>比如:</p>
<pre><code class="language-js">url += (url.includes('?') ? '&' : '?') + 'foo=bar'
</code></pre>
<p>使用<code>URL</code>构造函数我们可以这样写:</p>
<pre><code class="language-js">// url是一个URL构造函数
url.searchParams.set('foo', 'bar')
// 或者是一个字符串
const structuredUrl = new URL(url)
structuredUrl.searchParams.set('foo', 'bar')
url = structuredUrl.toString()
</code></pre>
<h2 id="读取url">读取URL</h2>
<p>现在,我如果想在没有库的情况下从当前 URL 读取查询参数这个由来已久的问题也得到了解决。</p>
<pre><code class="language-js">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()
</code></pre>
<p>这不仅限于浏览器,它也可以在 Node.js 中使用</p>
<pre><code class="language-js">const http = require('http');
const server = http.createServer((req, res) => {
const url = new URL(req.url, `https://${req.headers.host}`)
});
</code></pre>
<h2 id="url属性">URL属性</h2>
<p>URL 实例支持您已经在浏览器中使用的所有属性,例如 on<code>window.location</code>或 anchor 元素,所有这些我们都可以<em>读写</em>:</p>
<pre><code class="language-js">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
</code></pre>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/04eb3bc44a494f94b6a7b556c364f415~tplv-k3u1fbpfcp-watermark.image?" alt="4url.png" loading="lazy"></p>
<h2 id="常用urlsearchparams方法">常用URLSearchParams方法</h2>
<p>该<code>URLSearchParams</code>对象可在<code>URL</code>实例上访问,<code>url.searchParams</code>支持许多方便的方法:</p>
<h3 id="searchparamshasname">searchParams.has(name)</h3>
<blockquote>
<p>检查搜索参数是否包含给定名称</p>
</blockquote>
<pre><code class="language-js">url.searchParams.has('page') // true
</code></pre>
<h3 id="searchparamsgetname">searchParams.get(name)</h3>
<blockquote>
<p>获取给定参数的值</p>
</blockquote>
<pre><code class="language-js">url.searchParams.get('page') // '1'
</code></pre>
<h3 id="searchparamsgetallname">searchParams.getAll(name)</h3>
<blockquote>
<p>获取为参数提供的所有值。如果你允许同名的多个值,这很方便,例如<code>&page=1&page=2</code></p>
</blockquote>
<pre><code class="language-js">url.searchParams.getAll('page') // ['1']
</code></pre>
<h3 id="searchparamssetname-value">searchParams.set(name, value)</h3>
<blockquote>
<p>设置参数的值</p>
</blockquote>
<pre><code class="language-js">url.searchParams.set('page', '1')
</code></pre>
<h3 id="searchparamsappendname-value">searchParams.append(name, value)</h3>
<blockquote>
<p>附加一个参数——如果你可能多次支持同一个参数,这很有用,比如<code>&page=1&page=2</code></p>
</blockquote>
<pre><code class="language-js">url.searchParams.append('page', '2')
</code></pre>
<h3 id="searchparamsdeletename">searchParams.delete(name)</h3>
<blockquote>
<p>从 URL 中完全删除一个参数</p>
</blockquote>
<pre><code class="language-js">url.searchParams.delete('page')
</code></pre>
<h2 id="兼容性">兼容性</h2>
<p><code>new URL</code>基本支持所有现代浏览器(除了IE),以及 Node.js。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aa0f7701926c4a128631a39079cb8cde~tplv-k3u1fbpfcp-watermark.image?" alt="5urljr.png" loading="lazy"></p>
</div>
<div id="MySignature" role="contentinfo">
<div style="color: blueviolet">
<div>作者:前端南玖</div>
<div>出处:https://www.cnblogs.com/songyao666/
</div>
<div>每日面试题:Github</div>
<p>-------------------------------------------</p>
<p>如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新的文章~</p>
<p>扫描下方二维码关注公众号,回复进群,拉你进前端学习交流群</p>
<div style="display:flex">
<img src="https://blog-static.cnblogs.com/files/songyao666/nanjiu.gif?t=2" alt="逐梦wx" width="380" height="190"/>
</div>
</div><br><br>
来源:https://www.cnblogs.com/songyao666/p/17081749.html
頁:
[1]