HTMLRewriter 类允许开发者在 Cloudflare Workers 应用程序中构建全面且富有表现力的 HTML 解析器。可以将其视为直接在 Workers 应用程序中使用的类似 jQuery 的体验。借助强大的 JavaScript API 来解析和转换 HTML,HTMLRewriter 使开发者能够构建功能丰富的应用程序。
HTMLRewriter 类应在 Workers 脚本中实例化一次,并使用 on 和 onDocument 函数附加多个 handler。
new HTMLRewriter()
.on("*", new ElementHandler())
.onDocument(new DocumentHandler());在 HTMLRewriter API 中,许多属性和方法使用几种一致的类型:
-
Contentstring | Response | ReadableStream- 插入输出流的内容应为字符串、
Response或ReadableStream。
- 插入输出流的内容应为字符串、
-
ContentOptionsObject{ html: Boolean }控制 HTMLRewriter 处理插入内容的方式。如果html布尔值设为 true,内容将被视为原始 HTML。如果html布尔值设为 false 或未提供,内容将被视为文本并应用适当的 HTML 转义。
HTMLRewriter 可使用两种 handler 类型:元素 handler 和文档 handler。
元素 handler 响应任何传入元素,通过 HTMLRewriter 实例的 .on 函数附加。元素 handler 应响应 element、comments 和 text。以下示例使用 ElementHandler 类处理 div 元素。
class ElementHandler {
element(element) {
// An incoming element, such as `div`
console.log(`Incoming element: ${element.tagName}`);
}
comments(comment) {
// An incoming comment
}
text(text) {
// An incoming piece of text
}
}
async function handleRequest(req) {
const res = await fetch(req);
return new HTMLRewriter().on("div", new ElementHandler()).transform(res);
}文档 handler 表示传入的 HTML 文档。可以在文档 handler 上定义多个函数来查询和操作文档的 doctype、comments、text 和 end。与元素 handler 不同,文档 handler 的 doctype、comments、text 和 end 函数不受特定选择器范围限制。文档 handler 的函数会为页面上的所有内容调用,包括顶层 HTML 标签之外的内容:
class DocumentHandler {
doctype(doctype) {
// An incoming doctype, such as <!DOCTYPE html>
}
comments(comment) {
// An incoming comment
}
text(text) {
// An incoming piece of text
}
end(end) {
// The end of the document
}
}元素 handler 和文档 handler 上定义的所有函数都可以返回 void 或 Promise<void>。将 handler 函数设为 async 允许你访问外部资源,例如通过 fetch、Workers KV、Durable Objects 或缓存访问 API。
class UserElementHandler {
async element(element) {
let response = await fetch(new Request("/user"));
// fill in user info using response
}
}
async function handleRequest(req) {
const res = await fetch(req);
// run the user element handler via HTMLRewriter on a div with ID `user_info`
return new HTMLRewriter()
.on("div#user_info", new UserElementHandler())
.transform(res);
}element 参数仅在元素 handler 中使用,是 DOM 元素的表示。元素上有多个方法可用于查询和操作:
-
tagNamestring- 标签名称,例如
"h1"或"div"。此属性可以赋不同值以修改元素的标签。
- 标签名称,例如
-
attributesIterator read-only- 标签属性的
[name, value]对。
- 标签属性的
-
removedboolean- 指示元素是否已被之前的 handler 删除或替换。
-
namespaceURIstring- 表示元素的 namespace URI ↗。
getAttribute(name:string)string | null- 返回元素上给定属性名称的值,如果未找到则返回
null。
- 返回元素上给定属性名称的值,如果未找到则返回
hasAttribute(name:string)boolean- 返回布尔值,指示元素上是否存在某个属性。
setAttribute(name:string, valuestring)Element- 将属性设置为提供的值,如果属性不存在则创建它。
removeAttribute(name:string)Element- 删除属性。
before(content:Content, contentOptionsContentOptionsoptional)Element- 在元素之前插入内容。
after(content:Content, contentOptionsContentOptionsoptional)Element- 在元素之后立即插入内容。
prepend(content:Content, contentOptionsContentOptionsoptional)Element- 在元素开始标签之后立即插入内容。
append(content:Content, contentOptionsContentOptionsoptional)Element- 在元素结束标签之前立即插入内容。
replace(content:Content, contentOptionsContentOptionsoptional)Element- 删除元素并在其位置插入内容。
setInnerContent(content:Content, contentOptionsContentOptionsoptional)Element- 替换元素的内容。
remove():Element- 删除元素及其所有内容。
removeAndKeepContent():Element- 删除元素的开始标签和结束标签,但保留其内部内容。
onEndTag(handler:Function<void>)void- 注册在到达元素结束标签时调用的 handler。
endTag 参数仅在通过 element.onEndTag 注册的 handler 中使用,是 DOM 元素的有限表示。
namestring- 标签名称,例如
"h1"或"div"。此属性可以赋不同值以修改元素的标签。
- 标签名称,例如
before(content:Content, contentOptionsContentOptionsoptional)EndTag- 在结束标签之前插入内容。
after(content:Content, contentOptionsContentOptionsoptional)EndTag- 在结束标签之后立即插入内容。
remove():EndTag- 删除元素及其所有内容。
由于 Cloudflare 执行零拷贝流式解析,文本块与词法树中的文本节点不是同一概念。词法树文本节点可以由多个块表示,这些块从源站通过网络逐个到达。
考虑以下标记:<div>Hey. How are you?</div>。Workers 脚本可能不会一次性从源站收到整个文本节点;相反,text 元素 handler 会为文本节点的每个接收部分调用。例如,handler 可能先被调用 "Hey. How ",然后是 "are you?"。当最后一个块到达时,text 的 lastInTextNode 属性将设为 true。开发者应确保将这些块连接在一起。
-
removedboolean- 指示元素是否已被之前的 handler 删除或替换。
-
textstring read-only- 块的文本内容。如果块是文本节点的最后一个块,可能为空。
-
lastInTextNodeboolean read-only- 指定该块是否为文本节点的最后一个块。
before(content:Content, contentOptionsContentOptionsoptional)Element- 在元素之前插入内容。
after(content:Content, contentOptionsContentOptionsoptional)Element- 在元素之后立即插入内容。
replace(content:Content, contentOptionsContentOptionsoptional)Element- 删除元素并在其位置插入内容。
remove():Element- 删除元素及其所有内容。
元素 handler 上的 comments 函数允许开发者查询和操作 HTML 注释标签。
class ElementHandler {
comments(comment) {
// An incoming comment element, such as <!-- My comment -->
}
}-
comment.removedboolean- 指示元素是否已被之前的 handler 删除或替换。
-
comment.textstring- 注释的文本。此属性可以赋不同值以修改注释文本。
before(content:Content, contentOptionsContentOptionsoptional)Element- 在元素之前插入内容。
after(content:Content, contentOptionsContentOptionsoptional)Element- 在元素之后立即插入内容。
replace(content:Content, contentOptionsContentOptionsoptional)Element- 删除元素并在其位置插入内容。
remove():Element- 删除元素及其所有内容。
文档 handler 上的 doctype 函数允许开发者查询文档的 doctype ↗。
class DocumentHandler {
doctype(doctype) {
// An incoming doctype element, such as
// <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
}
}-
doctype.namestring | null read-only- doctype 名称。
-
doctype.publicIdstring | null read-only- doctype 中 PUBLIC 原子之后的引号字符串。
-
doctype.systemIdstring | null read-only- doctype 中 SYSTEM 原子之后或
publicId之后立即出现的引号字符串。
- doctype 中 SYSTEM 原子之后或
文档 handler 上的 end 函数允许开发者在文档末尾追加内容。
class DocumentHandler {
end(end) {
// The end of the document
}
}append(content:Content, contentOptionsContentOptionsoptional)DocumentEnd- 在文档结束之后插入内容。
以下是选择器及其用途。
-
*- 任何元素。
-
E- 任何 E 类型的元素。
-
E:nth-child(n)- E 元素,其父元素的第 n 个子元素。
-
E:first-child- E 元素,其父元素的第一个子元素。
-
E:nth-of-type(n)- E 元素,其同类型兄弟中的第 n 个。
-
E:first-of-type- E 元素,其同类型的第一个兄弟。
-
E:not(s)- 不匹配任一复合选择器的 E 元素。
-
E.warning- 属于 warning 类的 E 元素。
-
E#myid- ID 等于 myid 的 E 元素。
-
E[foo]- 具有 foo 属性的 E 元素。
-
E[foo="bar"]- foo 属性值完全等于 bar 的 E 元素。
-
E[foo="bar" i]- foo 属性值完全等于 bar 的任意(ASCII 范围)大小写排列的 E 元素。
-
E[foo="bar" s]- foo 属性值完全且大小写敏感地等于 bar 的 E 元素。
-
E[foo~="bar"]- foo 属性值为空格分隔的值列表且其中一项完全等于 bar 的 E 元素。
-
E[foo^="bar"]- foo 属性值以 bar 字符串开头的 E 元素。
-
E[foo$="bar"]- foo 属性值以 bar 字符串结尾的 E 元素。
-
E[foo*="bar"]- foo 属性值包含 bar 子字符串的 E 元素。
-
E[foo|="en"]- foo 属性值为以 en 开头的连字符分隔值列表的 E 元素。
-
E F- E 元素的后代 F 元素。
-
E > F- E 元素的子元素 F。
如果 handler 抛出异常,解析会立即停止,转换后的响应 body 会因抛出的异常而报错,未转换的响应 body 会被取消(关闭)。如果转换后的响应 body 已经部分流式传输回客户端,客户端将看到截断的响应。
async function handle(request) {
let oldResponse = await fetch(request);
let newResponse = new HTMLRewriter()
.on("*", {
element(element) {
throw new Error("A really bad error.");
},
})
.transform(oldResponse);
// At this point, an expression like `await newResponse.text()`
// will throw `new Error("A really bad error.")`.
// Thereafter, any use of `newResponse.body` will throw the same error,
// and `oldResponse.body` will be closed.
// Alternatively, this will produce a truncated response to the client:
return newResponse;
}