Koa 依赖的库 encodeurl 和 escape-html
本文为 koa 依赖系列文章,前几篇也可以在站内查看:
koa 中依赖的库 parseurl
Koa 依赖的库 type-is 和 content-disposition
Koa 依赖的库 accepts、content-type 和 cache-content-type
这次看两个字符串相关的库,在 Koa 中都只在 response 中的 redirect 函数内使用到。
encodeurl 用于 redirect 中 Location 的设置,当重定向时,response header 中有 Location 属性,值是一个 Url,在 koa 中此 url 使用 encodeurl 库进行编码,encodeurl 源码如下:
var ENCODE_CHARS_REGEXP = /(?:[^\x21\x25\x26-\x3B\x3D\x3F-\x5B\x5D\x5F\x61-\x7A\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g var UNMATCHED_SURROGATE_PAIR_REGEXP = /(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g var UNMATCHED_SURROGATE_PAIR_REPLACE = '$1\uFFFD$2' function encodeUrl (url) { return String(url) .replace(UNMATCHED_SURROGATE_PAIR_REGEXP, UNMATCHED_SURROGATE_PAIR_REPLACE) .replace(ENCODE_CHARS_REGEXP, encodeURI) }复制代码
可以看到 encodeurl 全部代码很少,最终调用的还是 encodeURI,不过在调用之前做了一些处理,这样已经被编码过的数据不会被重复编码,举个例子:
原始 url http://localhost:3001/te st
包含一个空格,此时需要进行编码,encode 之后的 url 变为 http://localhost:3001/te%20st
,此时空格被编码为 %20,如果将新的 url 再进行一次编码,百分号会被编码成 %25,此时编码结果为 http://localhost:3001/te%2520st
,原生的 encodeURI 方法只会对传入的字符串各个字符一一做编码转换,不会去判断是否已经经过了一次编码。作为一个通用的方法 encodeURI 这个逻辑是合理的,但是在 koa 中这个应用场景下已经编码过的 Url 不应该被再次编码,因此此处使用了 encodeurl 中的逻辑。
接下来看 escape-html 库,从名字可以看出这是一个过滤 html 字符串的工具,在浏览器中为了防止 XSS,对于从用户侧接收到的数据都要进行 escape 处理,这里 koa 中也是这个用途。在 redirect 逻辑中有这样一段代码:
if (this.ctx.accepts('html')) { url = escape(url) this.type = 'text/html; charset=utf-8' this.body = `Redirecting to <a href="${url}">${url}</a>.` return }复制代码
这里如果浏览器可以接收 html,koa 会在 body 添加一个 a 标签,这里会把 url 中的信息通过 a 标签渲染到页面上,由于 url 信息可能会包含特殊的 html 字符串,因此此处经过了一次 escape-html 处理,源码如下:
var matchHtmlRegExp = /["'&<>]/ function escapeHtml (string) { var str = '' + string var match = matchHtmlRegExp.exec(str) if (!match) { return str } var escape var html = '' var index = 0 var lastIndex = 0 for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: // " escape = '"' break case 38: // & escape = '&' break case 39: // ' escape = ''' break case 60: // < escape = '<' break case 62: // > escape = '>' break default: continue } if (lastIndex !== index) { html += str.substring(lastIndex, index) } lastIndex = index + 1 html += escape } return lastIndex !== index ? html + str.substring(lastIndex, index) : html }复制代码
代码逻辑本身不复杂,这里的匹配表达式为 /["'&<>]/
,把对应的五个特殊字符替换为安全的字符编码即可。
以上是两个字符串相关库的源码,这次内容很少,可以看出字符串处理离不开正则表达式的灵活应用,在前面 header 相关的库中也能看到很多相关的实践。
作者:丨隋堤倦客丨
链接:https://juejin.cn/post/7030970125331202062