API веб-платформы
Один из способов, которым Deno упрощает разработку веб- и облачных приложений, заключается в использовании стандартных API веб-платформы (например, fetch, WebSockets и других) вместо проприетарных API. Это означает, что если вы когда-либо разрабатывали для браузера, вам, вероятно, уже знакомо Deno, а если вы изучаете Deno, вы также расширяете свои знания о веб-технологиях.
Изучить поддерживаемые веб-API
Ниже мы рассмотрим некоторые стандартные веб-API, которые поддерживает Deno.
Чтобы проверить, доступен ли API веб-платформы в Deno, вы можете перейти по ссылке на интерфейс на MDN и обратиться к таблице совместимости с браузерами.
fetch
API fetch может использоваться для отправки HTTP-запросов. Он реализован в соответствии со спецификацией WHATWG fetch.
Отклонения от спецификации
- У пользователя Deno нет хранилища cookie. Следовательно, заголовок
set-cookieв ответе не обрабатывается и не отфильтровывается из видимых заголовков ответа. - Deno не следует политике одного происхождения, поскольку у агента пользователя Deno в настоящее время нет понятия происхождения и нет хранилища cookie. Это означает, что Deno не нужно защищать от утечки аутентифицированных данных через разные источники. По этой причине Deno не реализует следующие разделы спецификации WHATWG
fetch:- Раздел
3.1. 'Origin' header. - Раздел
3.2. CORS protocol. - Раздел
3.5. CORB. - Раздел
3.6. 'Cross-Origin-Resource-Policy' header. -
Atomic HTTP redirect handling. - Тип ответа
opaqueredirect.
- Раздел
fetchс режимомredirectmanualвернет ответbasic, а неopaqueredirect.- В спецификации неясно, как обрабатывать
file:URL-адреса. Firefox — единственный распространенный браузер, который реализует получениеfile:URL-адресов, и даже тогда это не работает по умолчанию. Начиная с Deno 1.16, Deno поддерживает загрузку локальных файлов. Подробности см. в следующем разделе. - Защита заголовков
requestиresponseреализована, но, в отличие от браузеров, не имеет ограничений на разрешенные имена заголовков. - Свойства
referrer,referrerPolicy,mode,credentials,cache,integrity,keepalive, иwindowи их соответствующие поведения вRequestInitне реализованы. Соответствующие поля отсутствуют в объектеRequest. - Поддержка потоковой передачи загрузки тела запроса (в HTTP/1.1 и HTTP/2). В отличие от текущего предложения fetch, реализация поддерживает двунаправленную потоковую передачу.
- Заголовок
set-cookieне конкатенируется при итерации по итераторуheaders. Это поведение находится в процессе спецификации на стадии спецификации.
Загрузка локальных файлов
Deno поддерживает загрузку file: URL-адресов. Это упрощает написание кода, который использует один и тот же путь кода на сервере и локально, а также упрощает создание кода, который работает как с Deno CLI, так и с Deno Deploy.
Deno поддерживает только абсолютные URL-адреса файлов, это означает, что fetch("./some.json") не будет работать. Тем не менее, следует отметить, что если указан --location, относительные URL-адреса используют --location в качестве базового, но file: URL не может быть передан в качестве --location.
Для загрузки ресурса, относительного к текущему модулю, который будет работать как локально, так и удаленно, следует использовать import.meta.url в качестве базового. Например:
const response = await fetch(new URL("./config.json", import.meta.url));
const config = await response.json();
Примечания по загрузке локальных файлов:
- Для чтения ресурсов применяются разрешения, поэтому необходимо соответствующее разрешение
--allow-readдля чтения локального файла. - Локальная загрузка поддерживает только метод
GET, а любой другой метод отклонит обещание. - Файла, которого не существует, просто отклоняет обещание с неясным сообщением
TypeError. Это сделано для предотвращения потенциальных атак на определение. - В ответе не задаются заголовки. Поэтому потребителю необходимо самостоятельно определять такие вещи, как тип содержимого или длина содержимого.
- Тела ответов передаются потоком со стороны Rust, поэтому большие файлы доступны по частям и могут быть отменены.
CustomEvent и EventTarget
API DOM Event можно использовать для отправки и прослушивания событий, происходящих в приложении. Он реализован в соответствии со спецификацией WHATWG DOM.
Отклонения от спецификации
- События не распространяются, так как у Deno нет иерархии DOM, поэтому нет дерева для распространения/захвата событий.
-
Свойство
timeStampвсегда устанавливается в значение0.
Типизация
Определения TypeScript для реализованных веб-API можно найти в файлах lib.deno.shared_globals.d.ts и lib.deno.window.d.ts.
Определения, специфичные для рабочих процессов, можно найти в файле lib.deno.worker.d.ts.
Location
Deno поддерживает глобальный объект location из веб-среды.
Флаг Location
В процессе Deno нет «веб-страницы», URL которой мы можем использовать для расположения. Вместо этого мы позволяем пользователям эмулировать расположение документа, указав его в командной строке с флагом --location. Это может быть http или https URL-адрес.
// deno run --location https://example.com/path main.ts
console.log(location.href);
// "https://example.com/path"
Для работы необходимо передать --location <href> . Если этого не сделать, любой доступ к глобальному объекту location вызовет ошибку.
// deno run main.ts
console.log(location.href);
// error: Uncaught ReferenceError: Access to "location", run again with --location <href>.
Установка location или любого из его полей обычно вызывает навигацию в браузерах. Это не применимо в Deno, поэтому в этом случае будет выброшена ошибка.
// deno run --location https://example.com/path main.ts
location.pathname = "./foo";
// error: Uncaught NotSupportedError: Cannot set "location.pathname".
Расширенное использование
В веб-среде разрешение ресурсов (за исключением модулей) обычно использует значение location.href в качестве корня для базирования относительных URL-адресов. Это влияет на некоторые веб-API, принятые Deno.
API fetch
// deno run --location https://api.github.com/ --allow-net main.ts
const response = await fetch("./orgs/denoland");
// Fetches "https://api.github.com/orgs/denoland".
Вызов fetch() выше выбросит ошибку, если не был передан флаг --location, поскольку нет аналога веб-расположения для его базирования.
Модули рабочих процессов
// deno run --location https://example.com/index.html --allow-net main.ts
const worker = new Worker("./workers/hello.ts", { type: "module" });
// Fetches worker module at "https://example.com/workers/hello.ts".
Для вышеперечисленных случаев предпочтительнее передавать URL-адреса полностью, а не полагаться на --location. При необходимости вы можете вручную создать относительный URL-адрес с помощью конструктора URL.
Флаг --location предназначен для тех, кто преследует определенную цель эмуляции расположения документа и понимает, что это будет работать только на уровне приложения. Однако вы также можете использовать его для подавления ошибок от зависимостей, которые без необходимости обращаются к глобальному объекту location.
Web Storage
API Web Storage предоставляет API для хранения строковых ключей и значений. Сохранение данных работает аналогично браузеру и имеет лимит хранения 10 МБ. Глобальный объект sessionStorage сохраняет данные только для текущего контекста выполнения, в то время как localStorage сохраняет данные от выполнения к выполнению.
В браузере localStorage сохраняет данные уникально для каждого происхождения (по сути, протокол плюс имя хоста плюс порт). Начиная с Deno 1.16, Deno имеет набор правил для определения уникального места хранения данных:
- При использовании флага
--location, происхождение расположения используется для уникального хранения данных. Это означает, что расположенияhttp://example.com/a.ts,http://example.com/b.tsиhttp://example.com:80/будут использовать одно и то же хранилище, ноhttps://example.com/будет другим. - Если указатель расположения отсутствует, но указан файл конфигурации
--config, абсолютный путь к этому файлу конфигурации используется. Это означает, чтоdeno run --config deno.jsonc a.tsиdeno run --config deno.jsonc b.tsбудут использовать одно и то же хранилище, ноdeno run --config tsconfig.json a.tsбудет другим. - Если нет файла конфигурации или указателя расположения, Deno использует абсолютный путь к главному модулю для определения общего хранилища. Deno REPL генерирует «синтетический» главный модуль, основанный на текущей рабочей директории, из которой запускается
deno. Это означает, что несколько запусков REPL из одного пути будут использовать одни и те же сохраненные данныеlocalStorage.
Для установки, получения и удаления элементов из localStorage, можно использовать следующее:
// Set an item in localStorage
localStorage.setItem("myDemo", "Deno App");
// Read an item from localStorage
const cat = localStorage.getItem("myDemo");
// Remove an item from localStorage
localStorage.removeItem("myDemo");
// Remove all items from localStorage
localStorage.clear();
Web Workers
Deno поддерживает Web Worker API.
Рабочие процессы могут использоваться для запуска кода на нескольких потоках. Каждый экземпляр Worker выполняется в отдельном потоке, предназначенном только для этого рабочего процесса.
В настоящее время Deno поддерживает только рабочие процессы типа module, поэтому при создании нового рабочего процесса необходимо передать опцию type: "module".
Использование относительных указателей модулей в главном рабочем процессе поддерживается только при передаче --location <href> в командной строке. Это не рекомендуется для переносимости. Вместо этого можно использовать конструктор URL и import.meta.url для лёгкого создания указателя для ближайшего скрипта. Однако у выделенных рабочих процессов есть расположение и эта возможность по умолчанию.
// Good
new Worker(import.meta.resolve("./worker.js"), { type: "module" });
// Bad
new Worker(import.meta.resolve("./worker.js"));
new Worker(import.meta.resolve("./worker.js"), { type: "classic" });
new Worker("./worker.js", { type: "module" });
Как и для обычных модулей, в модулях рабочих процессов можно использовать верхнеуровневые await. Однако следует быть осторожным и всегда регистрировать обработчик сообщений до первого await, так как в противном случае сообщения могут быть потеряны. Это не ошибка Deno, а всего лишь неприятное взаимодействие функций, и это также происходит во всех браузерах, поддерживающих модульные рабочие процессы.
import { delay } from "jsr:@std/async@1/delay";
// First await: waits for a second, then continues running the module.
await delay(1000);
// The message handler is only set after that 1s delay, so some of the messages
// that reached the worker during that second might have been fired when no
// handler was registered.
self.onmessage = (evt) => {
console.log(evt.data);
};
Разрешения на создание экземпляров
Создание нового Worker экземпляра похоже на динамический импорт; поэтому Deno требует соответствующих разрешений для этого действия.
Для рабочих процессов, использующих локальные модули; требуется разрешение --allow-read.
new Worker(import.meta.resolve("./worker.ts"), { type: "module" });
console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: read access to "./worker.ts", run again with the --allow-read flag
$ deno run --allow-read main.ts
hello world
Для рабочих процессов, использующих удалённые модули; требуется разрешение --allow-net.
new Worker("https://example.com/worker.ts", { type: "module" });
// This file is hosted at https://example.com/worker.ts
console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: net access to "https://example.com/worker.ts", run again with the --allow-net flag
$ deno run --allow-net main.ts
hello world
Использование Deno в рабочем процессе
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
});
worker.postMessage({ filename: "./log.txt" });
self.onmessage = async (e) => {
const { filename } = e.data;
const text = await Deno.readTextFile(filename);
console.log(text);
self.close();
};
hello world
$ deno run --allow-read main.js
hello world
Указание разрешений для рабочего процесса
Это нестабильная функция Deno. Узнайте больше о нестабильных функциях.
Разрешения, доступные для рабочего процесса, аналогичны флагам разрешений командной строки, что означает, что каждое включенное разрешение можно отключить на уровне API рабочего процесса. Более подробное описание каждого из параметров разрешений можно найти здесь.
По умолчанию рабочий процесс наследует разрешения из потока, в котором он был создан, однако для ограничения доступа этого рабочего процесса мы предоставляем опцию deno.permissions в API рабочего процесса.
Для разрешений, которые поддерживают гранулированный доступ, вы можете передать список требуемых ресурсов, к которым будет иметь доступ рабочий процесс, а для тех, которые поддерживают только включение/выключение, вы можете передать true/false соответственно:
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
deno: {
permissions: {
net: [
"deno.land",
],
read: [
new URL("./file_1.txt", import.meta.url),
new URL("./file_2.txt", import.meta.url),
],
write: false,
},
},
});
Разрешения с гранулированным доступом принимают абсолютные и относительные пути в качестве аргументов, однако следует учитывать, что относительные пути будут разрешаться относительно файла, в котором создаётся рабочий процесс, а не пути к файлу самого рабочего процесса:
const worker = new Worker(
new URL("./worker/worker.js", import.meta.url).href,
{
type: "module",
deno: {
permissions: {
read: [
"/home/user/Documents/deno/worker/file_1.txt",
"./worker/file_2.txt",
],
},
},
},
);
Как deno.permissions, так и его дочерние элементы поддерживают опцию "inherit", что подразумевает, что они унаследуют разрешения своего родительского элемента:
// This worker will inherit its parent permissions
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
deno: {
permissions: "inherit",
},
});
// This worker will inherit only the net permissions of its parent
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
deno: {
permissions: {
env: false,
hrtime: false,
net: "inherit",
ffi: false,
read: false,
run: false,
write: false,
},
},
});
Если не указана опция deno.permissions или один из её дочерних элементов, рабочий процесс по умолчанию наследует разрешения:
// This worker will inherit its parent permissions
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
});
// This worker will inherit all the permissions of its parent BUT net
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
deno: {
permissions: {
net: false,
},
},
});
Вы можете полностью отключить разрешения рабочего процесса, передав "none" опции deno.permissions.
// This worker will not have any permissions enabled
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
deno: {
permissions: "none",
},
});
Отклонения других API от спецификации
API кэша
Реализованы только следующие API:
- CacheStorage::open()
- CacheStorage::has()
- CacheStorage::delete()
- Cache::match()
- Cache::put()
- Cache::delete()
Несколько отличий по сравнению с браузерами:
- Вы не можете передавать относительные пути в API. Запрос может быть экземпляром Request или URL или строкой URL.
-
match()иdelete()пока не поддерживают параметры запроса.
© 2018–2024 the Deno authors
Licensed under the MIT License.
https://docs.deno.com/runtime/reference/web_platform_apis