← Cloudflare Workers / workers / runtime-apis
HTMLRewriter
Контекст
HTMLRewriter класс позволяет разработчикам создавать полноценные и выразительные HTML парсеры внутри приложения Cloudflare Workers. Его можно рассматривать как аналог jQuery прямо внутри вашего Workers приложения. Опираясь на мощный JavaScript API для разбора и преобразования HTML, HTMLRewriter позволяет разработчикам создавать функционально насыщенные приложения.
HTMLRewriter класс следует инстанцировать один раз в вашем Workers скрипте, присоединив ряд обработчиков с помощью on и onDocument функции.
Конструктор
new HTMLRewriter()
.on("*", new ElementHandler())
.onDocument(new DocumentHandler());Глобальные типы
На протяжении HTMLRewriter API есть несколько стандартных типов, которые используются во многих свойствах и методах:
-
Contentstring | Response | ReadableStream- Содержимое, добавляемое в выходной поток, должно быть строкой,
Response, илиReadableStream.
- Содержимое, добавляемое в выходной поток, должно быть строкой,
-
ContentOptionsObject{ html: Boolean }Определяет, как HTMLRewriter обрабатывает вставленный контент. Еслиhtmlboolean имеет значение true, содержимое обрабатывается как необработанный HTML. Еслиhtmlboolean имеет значение false или не указан, содержимое будет обработано как обычный текст с соответствующим экранированием HTML.
Обработчики
Есть два типа обработчиков, которые можно использовать с HTMLRewriter: обработчики элементов и обработчики документов.
Element Handlers
Обработчик элементов реагирует на любой входящий элемент, если подключён с помощью .on функцию у HTMLRewriter экземпляр. Обработчик элемента должен реагировать на element, comments, а также text. В примере обрабатывается div элементов с ElementHandler в качестве класса.
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);
}Обработчики документа
Обработчик документа представляет входящий HTML документ. В обработчике документа можно определить ряд функций для запроса и изменения его doctype, comments, text, а также end. В отличие от обработчика элемента, у обработчика документа doctype, comments, text, а также end функции не ограничены конкретным селектором. Функции обработчика документа вызываются для всего содержимого страницы, включая содержимое за пределами тега верхнего уровня 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
}
}Асинхронные обработчики
Все функции, определённые как в обработчиках элементов, так и в обработчиках документа, могут возвращать либо void или Promise<void>. Если сделать функцию-обработчик async позволяет обращаться к внешним ресурсам, например к API через fetch, к Workers KV, Durable Objects или кешу.
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 аргумент, используемый только в обработчиках элементов, представляет собой представление DOM-элемента. У элемента есть ряд методов для его изучения и изменения:
Свойства
-
tagNameстрока- Имя тега, например
"h1"или"div". Этому свойству можно присваивать разные значения, чтобы изменить тег элемента.
- Имя тега, например
-
attributesIterator только для чтения- A
[name, value]пару атрибутов тега.
- A
-
removedboolean- Указывает, был ли элемент удалён или заменён одним из предыдущих обработчиков.
-
namespaceURIстрока- Представляет 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- Регистрирует обработчик, который вызывается при достижении закрывающего тега элемента.
EndTag
endTag аргумент, используемый только в обработчиках, зарегистрированных с помощью element.onEndTag, является ограниченным представлением элемента DOM.
Свойства
nameстрока- Имя тега, например
"h1"или"div". Этому свойству можно присваивать разные значения, чтобы изменить тег элемента.
- Имя тега, например
Методы
-
before(content:Content, contentOptionsContentOptionsoptional)EndTag- Вставляет содержимое сразу перед закрывающим тегом.
-
after(content:Content, contentOptionsContentOptionsoptional)EndTag- Вставляет содержимое сразу после закрывающего тега.
-
remove():EndTag- Удаляет элемент вместе со всем его содержимым.
Текстовые фрагменты
Поскольку Cloudflare выполняет потоковый парсинг без копирования (zero-copy), текстовые фрагменты не совпадают с текстовыми узлами лексического дерева. Один текстовый узел лексического дерева может быть представлен несколькими фрагментами по мере их поступления от источника по сети.
Рассмотрим следующую разметку: <div>Hey. How are you?</div>. Возможно, что скрипт Workers не получит весь текстовый узел от источника целиком за один раз, вместо этого text обработчик элемента будет вызываться для каждой полученной части текстового узла. Например, обработчик может быть вызван с "Hey. How ", затем "are you?". Когда приходит последний chunk, у текста lastInTextNode свойство будет установлено в true. Разработчикам следует объединять эти chunks между собой.
Свойства
-
removedboolean- Указывает, был ли элемент удалён или заменён одним из предыдущих обработчиков.
-
textстрока только для чтения- Текстовое содержимое фрагмента. Может быть пустым, если это последний фрагмент текстового узла.
-
lastInTextNodeboolean, только для чтения- Определяет, является ли этот фрагмент последним фрагментом текстового узла.
Методы
-
before(content:Content, contentOptionsContentOptionsoptional)Element- Вставляет содержимое перед элементом.
-
after(content:Content, contentOptionsContentOptionsoptional)Element- Вставляет содержимое сразу после элемента.
-
replace(content:Content, contentOptionsContentOptionsoptional)Element- Удаляет элемент и вставляет содержимое на его место.
-
remove():Element- Удаляет элемент вместе со всем его содержимым.
Комментарии
comments функция обработчика элемента позволяет разработчикам запрашивать HTML-комментарии и управлять ими.
class ElementHandler {
comments(comment) {
// An incoming comment element, such as <!-- My comment -->
}
}Свойства
-
comment.removedboolean- Указывает, был ли элемент удалён или заменён одним из предыдущих обработчиков.
-
comment.textстрока- Текст комментария. Этому свойству можно присваивать разные значения, чтобы изменить текст комментария.
Методы
-
before(content:Content, contentOptionsContentOptionsoptional)Element- Вставляет содержимое перед элементом.
-
after(content:Content, contentOptionsContentOptionsoptional)Element- Вставляет содержимое сразу после элемента.
-
replace(content:Content, contentOptionsContentOptionsoptional)Element- Удаляет элемент и вставляет содержимое на его место.
-
remove():Element- Удаляет элемент вместе со всем его содержимым.
Doctype
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 только для чтения- Имя doctype.
-
doctype.publicIdstring | null только для чтения- Строка в кавычках в doctype после атома PUBLIC.
-
doctype.systemIdstring | null только для чтения- Строка в кавычках в doctype после атома SYSTEM или сразу после
publicId.
- Строка в кавычках в doctype после атома SYSTEM или сразу после
Конец
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- Элемент E, принадлежащий классу warning.
-
E#myid- Элемент E с ID, равным myid.
-
E[foo]- Элемент E с атрибутом foo.
-
E[foo="bar"]- Элемент E, значение атрибута foo которого точно равно bar.
-
E[foo="bar" i]- Элемент E, значение атрибута foo которого точно равно любому варианту написания bar с учётом регистра (в диапазоне ASCII).
-
E[foo="bar" s]- Элемент E, значение атрибута foo которого точно и с учётом регистра равно bar.
-
E[foo~="bar"]- Элемент E, значение атрибута foo которого представляет собой список значений через пробел, одно из которых точно равно bar.
-
E[foo^="bar"]- Элемент E, значение атрибута foo которого начинается точно со строки bar.
-
E[foo$="bar"]- Элемент E, значение атрибута foo которого заканчивается точно строкой bar.
-
E[foo*="bar"]- Элемент E, значение атрибута foo которого содержит подстроку bar.
-
E[foo|="en"]- Элемент E, значение атрибута foo которого представляет собой список значений через дефис, начинающийся с en.
-
E F- Элемент F, являющийся потомком элемента E.
-
E > F- Элемент F, дочерний элемент элемента E.
Ошибки
Если обработчик выбрасывает исключение, разбор немедленно останавливается, тело преобразованного ответа завершается с этим исключением, а тело непреобразованного ответа отменяется (закрывается). Если часть тела преобразованного ответа уже была передана клиенту, клиент получит усечённый ответ.
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;
}