Удивительный Deno Начало работы и практическое применение

внешний интерфейс deno
Удивительный Deno Начало работы и практическое применение

Отличный учебник по Deno

1. Введение в Дено

Deno — это среда выполнения JavaScript/TypeScript, которая по умолчанию выполняет код в безопасной среде и имеет отличный опыт разработки. Deno включает в себя следующие особенности:

  • Безопасность по умолчанию. Внешний код не имеет доступа к файловой системе, сети или среде, если это явно не разрешено.

  • Среда, которая поддерживает TypeScript из коробки.

  • Распространяйте только автономный исполняемый файл (deno).

  • Имеет встроенные наборы инструментов, такие как средство просмотра информации о зависимостях (deno info) и инструмент форматирования кода (deno fmt).

  • Существует набор проверенных стандартных модулей, которые гарантированно работают на Deno.

  • Код скрипта может быть упакован как один файл JavaScript.

Deno — это кроссплатформенная среда выполнения, среда выполнения, основанная на движке Google V8, разработанная с использованием языка Rust и использующая библиотеку Tokio для построения системы циклов событий. Deno построен на основе V8, Rust и Tokio и построен следующим образом:

deno-schematic-diagram

(Источник изображения:Нет. слишком ленив для /manual/cont…

1.1 Rust

Rust — это компилируемый язык программирования общего назначения, разработанный Mozilla. Принцип проектирования «безопасный, параллельный, практичный» и поддерживает функциональные, параллельные, процедурные и объектно-ориентированные стили программирования. Deno использует язык Rust для инкапсуляции двигателя V8 черезlibdenoBinding, мы можем вызывать изолированные функции в JavaScript.

1.2 Tokio

Tokio — это асинхронная среда выполнения для языка программирования Rust, предоставляющая асинхронную управляемую событиями платформу для создания быстрых, надежных и легких веб-приложений. Воспользуйтесь преимуществами модели владения и параллелизма Rust, чтобы обеспечить безопасность потоков. Построенный на Rust, Tokio обеспечивает чрезвычайно высокую производительность, что делает его идеальным для высокопроизводительных серверных приложений. В Deno Tokio используется для параллельного выполнения всех задач асинхронного ввода-вывода.

1.3 V8

V8 — это движок JavaScript с открытым исходным кодом, разработанный Google и используемый в Google Chrome и Chromium. V8 повышает производительность, компилируя JavaScript в машинный код, а не в байт-код, или интерпретируя его перед запуском. Кроме того, для повышения производительности используются такие методы, как встроенное кэширование. Благодаря этим функциям программы на JavaScript сравнимы с двоичной компиляцией по скорости движка V8. В Deno для выполнения кода JavaScript используется движок V8.

2. Установите Дено

Deno работает на macOS, Linux и Windows. Deno — это автономный исполняемый файл, он не имеет дополнительных зависимостей. Вы можете установить его:

  • Использование Shell (macOS и Linux):
curl -fsSL https://deno.land/x/install/install.sh | sh
  • Использование PowerShell (Windows):
iwr https://deno.land/x/install/install.ps1 -useb | iex
  • использоватьScoop(Windows):
scoop install deno
choco install deno
  • использоватьHomebrew(макОС):
brew install deno
  • использоватьCargo(Windows, macOS, Linux):
cargo install deno

Deno также можно установить вручную, просто изGitHub.com/Ноланд/…Загрузите zip-файл. Он просто содержит один исполняемый файл. В macOS и Linux вам необходимо установить для него разрешение на выполнение. После успешной установки вы можете выполнитьdeno --versionКоманда для просмотра установленной версии Deno:

$ deno --version
deno 1.0.0
v8 8.4.300
typescript 3.9.2

2.1 deno_install

В процессе установки, если у вас возникнут проблемы, вы можете попробоватьпростоявакСценарий установки предоставлен великим богом ——deno_install. Этот скрипт устанавливает Deno в систему с помощью однострочной команды (внутреннее ускорение).

2.1.1 Установите последнюю версию

Использование оболочки:

curl -fsSL https://x.deno.js.cn/install.sh | sh

Использование PowerShell:

iwr https://x.deno.js.cn/install.ps1 -useb -outf install.ps1; .\install.ps1
# iwr https://x.deno.js.cn/install.ps1 -useb | iex  
2.1.2 Установка определенной версии

Использование оболочки:

curl -fsSL https://x.deno.js.cn/install.sh | sh -s v0.41.0

Использование PowerShell:

iwr https://x.deno.js.cn/install.ps1 -useb -outf install.ps1; .\install.ps1 v0.41.0

Для получения более подробной информации, пожалуйста, посетитеx.deno.js.cnсайт.

2.2 deno-cli

Интерфейс командной строки deno-cli предоставляет набор интегрированных функций, позволяющих полностью погрузиться в проприетарную среду разработки Deno. Ниже перечислены все подкоманды, поддерживаемые Deno 1.0.0:

SUBCOMMANDS:
  bundle         Bundle module and dependencies into single file
  cache          Cache the dependencies
  completions    Generate shell completions
  doc            Show documentation for a module
  eval           Eval script
  fmt            Format source files
  help           Prints this message or the help of the given subcommand(s)
  info           Show info about cache or info related to source file
  install        Install script as an executable
  repl           Read Eval Print Loop
  run            Run a program given a filename or url to the module
  test           Run tests
  types          Print runtime TypeScript declarations
  upgrade        Upgrade deno executable to given version

2.3 REPL

Введите команду deno в команду, и вы запуститеREPL (цикл чтения-выполнения-печати):

$ deno
Deno 1.0.0
exit using ctrl+d or close()
> 1 + 2
3
> const name = "semlinker";
undefined
> console.log(name);
semlinker
undefined

2.4 VSCode Deno extension

Я считаю, что многие мелкие партнеры используют VSCode IDE для фронтенд-разработки.Для разработчиков, которые изучают и разрабатывают Deno, они не должны это пропустить.простоявакРазработано БогомVisual Studio Code Deno extension.

2.4.1 Расширение Deno не установлено

если мы напишемfrom "./hello.ts"Для такого утверждения в VSCode появится сообщение об ошибке с тильдой. Поскольку по умолчанию в проекты TypeScript не нужно добавлять.tsимя расширения.

ts(2691): An import path cannot end with a '.ts' extension. Consider importing './hello' instead.

ts-error-2691-1

ts-error-2691-2

Deno позволяет импортировать модули из URL-адресов, но TypeScript не поддерживает импорт модулей из URL-адресов.

ts(2307): Cannot find module 'Нет. Слишком ленивый / маленький / физический магазин / журнал / нет ...'.

ts-error-2307

2.4.2 Расширение Deno установлено

Deno кэширует удаленный импорт в$DENO_DIRв специальном каталоге, указанном переменной среды. если не указано$DENO_DIR, по умолчанию используется системный каталог кэша.

Плагин может разрешать удаленный импорт по локальным путям.

with-deno-plugin

(Источник изображения для этой главы:marketplace.visual studio.com/items?item N…

Для получения более подробной информации о расширении VSCode Deno вы можете прочитатьпростоявакбольшой пареньЯ разработал плагин Deno для VS Code.Эта статья.

3. Первый опыт Deno

3.1 welcome demo

Я считаю, что некоторым читателям не терпится установить Deno, и теперь мы сразу познакомимся с приложением Deno. Сначала откройте знакомую вам командную строку, а затем введите в командной строке следующую команду:

$ deno run https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using master branch https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕

Наблюдая за приведенным выше выводом, мы можем знать, что при запускеdeno run https://deno.land/std/examples/welcome.tsПосле команды Deno запуститсяhttps://deno.land/std/examples/welcome.tsURL-адрес загрузкиwelcome.tsфайл, содержимое которого:

console.log("Welcome to Deno 🦕");

После успешной загрузки файла Denowelcome.tsФайл компилируется, то есть компилируется вwelcome.ts.jsфайл, а затем выполнить скомпилированный файл JavaScript через механизм V8. Следует отметить, что если вы повторно запустите вышеуказанную команду в командной строке, файлы, которые были сгенерированы в кеше, будут выполнены и больше не будут загружаться из Интернета.welcome.tsдокумент.

$ deno run https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕

Как доказать, что при повторном выполнении вышеуказанной команды Deno предпочтительнее выполнит скомпилированный файл JavaScript в кеше? Здесь мы впервые представимdeno infoкоманда для отображения информации о кеше или исходных файлах:

$ deno info
DENO_DIR location: "/Users/fer/Library/Caches/deno"
Remote modules cache: "/Users/fer/Library/Caches/deno/deps"
TypeScript compiler cache: "/Users/fer/Library/Caches/deno/gen"

В приведенном выше выводе мы видимTypeScript compiler cacheЭта строка записей, очевидно, является каталогом, кэшированным компилятором TypeScript.После входа в этот каталог путем послойного поиска мы, наконец, находимexamplesнашел в каталогеwelcome.ts.jsдокумент:

➜  examples ls
welcome.ts.js     welcome.ts.js.map welcome.ts.meta

открыть каталогwelcome.ts.jsфайла мы видим следующее:

"use strict";
console.log("Welcome to Deno 🦕");
//# sourceMappingURL=file:///Users/fer/Library/Caches/deno/gen/https/deno.land/std/examples/welcome.ts.js.map

Теперь изменим файл и добавим в файл строку выходной информацииconsole.log("Hello Semlinker, from Cache");, подробности следующим образом:

"use strict";
console.log("Hello Semlinker, from Cache");
console.log("Welcome to Deno 🦕");
//# sourceMappingURL=file:///Users/fer/Library/Caches/deno/gen/https/deno.land/std/examples/welcome.ts.js.map

Затем мы повторно выполняем следующую команду в командной строке:

$ deno run https://deno.land/std/examples/welcome.ts
Hello Semlinker, from Cache
Welcome to Deno 🦕

Итак, теперь снова возникает вопрос, как заставить обновить кеш, то есть перекомпилировать код TypeScript? Для этой проблемы запуститеdeno runкоманда, нам нужно добавить--reloadфлаг, чтобы сообщить Deno, что указанный файл необходимо обновить:

$ deno run --reload https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using master branch https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕

Кроме--reloadВ дополнение к флагам команда запуска Deno также поддерживает множество других флагов, и заинтересованные читатели могут запуститьdeno run --helpкоманда для просмотра дополнительной информации.

3.2 TCP echo server

Мы уже представили, как запустить официальнуюwelcomeВ качестве примера давайте представим, как создать простой эхо-сервер TCP с помощью Deno. Сначала мы создаемlearn-denoпроект, а затем создать новый под проектомquickstartкаталог, затем создайте новыйecho_server.tsфайл и введите следующий код:

const listener = Deno.listen({ port: 8080 });
console.log("listening on 0.0.0.0:8080");
for await (const conn of listener) {
  Deno.copy(conn, conn);
}

Оператор for await...of создает итеративный цикл по асинхронным или синхронным итерируемым объектам, включая объекты типа String, Array, Array (такие как аргументы или NodeList), TypedArray, Map, Set и пользовательские асинхронные или синхронные итерируемые объекты. .

Синтаксис for await...of следующий:

for await (variable of iterable) {
statement
}

Я полагаю, что после ввода приведенного выше кода многие читатели, как и я, будут запускать следующие команды непосредственно в командной строке:

➜  quickstart deno run ./echo_server.ts 
Compile file:///Users/fer/LearnProjects/learn-deno/quickstart/echo_server.ts
error: Uncaught PermissionDenied: network access to "0.0.0.0:8080", run again with the --allow-net flag
    at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
    at Object.sendSync ($deno$/ops/dispatch_json.ts:72:10)
    at Object.listen ($deno$/ops/net.ts:51:10)
    at Object.listen ($deno$/net.ts:152:22)
    at file:///Users/fer/LearnProjects/learn-deno/quickstart/echo_server.ts:1:23

Очевидно, это ошибка разрешения, из сообщения об ошибке Дено говорит нам, что нам нужно установить--allow-netфлаг, чтобы разрешить доступ к сети. Почему это так? Это связано с тем, что Deno — это среда выполнения JavaScript/TypeScript, которая по умолчанию выполняет код в безопасной среде. Ниже мы добавляем--allow-netотметьте, затем снова запуститеecho_server.tsдокумент:

➜  quickstart deno run --allow-net ./echo_server.ts
listening on 0.0.0.0:8080

После успешного запуска сервера мы используемncКоманда для проверки работоспособности сервера:

➜  ~ nc localhost 8080
hell semlinker
hell semlinker

После ознакомления с тем, как использовать Deno для создания простого эхо-сервера TCP, давайте представим, как использовать Deno для создания простого HTTP-сервера.

3.3 HTTP Server

Как и TCP-сервер, вquickstartВ каталоге создаем новыйhttp_server.tsфайл и введите следующее:

import { serve } from "https://deno.land/std@v0.50.0/http/server.ts";

const PORT = 8080;
const s = serve({ port: PORT });

console.log(` Listening on <http://localhost>:${PORT}/`);

for await (const req of s) {
  req.respond({ body: "Hello Semlinker\\n" });
}

Дружеское напоминание: в реальном процессе разработки вы можетеdeno.land/stdадрес для получения требуемой стандартной версии библиотеки. В примере мы явно указываем версию, конечно можно и не указывать версию, например:нет.слишком ленивый/магазин/http/color….

В приведенном выше коде мы импортируем функцию serve в модуль http стандартной библиотеки Deno, а затем используем эту функцию для быстрого создания HTTP-сервера.Функция определяется следующим образом:

// std/http/server.ts
export function serve(addr: string | HTTPOptions): Server {
  if (typeof addr === "string") {
    const [hostname, port] = addr.split(":");
    addr = { hostname, port: Number(port) };
  }

  const listener = listen(addr);
  return new Server(listener);
}

Функция serve принимает один параметр, тип которогоstring | HTTPOptions, где интерфейс HTTPOptions определяется следующим образом:

/** Options for creating an HTTP server. */
export type HTTPOptions = Omit<Deno.ListenOptions, "transport">;

export interface ListenOptions {
    /** The port to listen on. */
    port: number;
    /** A literal IP address or host name that can be resolved to an IP address.
     * If not specified, defaults to `0.0.0.0`. */
    hostname?: string;
}

Когда тип входного параметра является строкой, функция serve будет использовать:Двоеточие обрезает строку, получает имя хоста и порт, затем оборачивает их в объект и присваивает их параметру addr, а затем использует параметр addr для продолжения вызова.listenфункция для создания дополнительныхlistenerобъект, последний вызовnew Server(listener)Создайте HTTP-сервер.

После создания HTTP-сервера запустим сервер, откроем командную строку и введем следующую команду:

➜  quickstart deno run --allow-net ./http_server.ts 
Compile file:///Users/fer/LearnProjects/learn-deno/quickstart/http_server.ts
 Listening on <http://localhost>:8080/

Затем откройте браузер и введите в адресную строкуhttp://localhost:8080/адрес, то на текущей странице вы увидите следующее:

Hello World\n

В-четвертых, отладка Deno

Поддержка ДеноV8 Inspector Protocol. Программы Deno можно отлаживать с помощью Chrome Devtools или других клиентов, поддерживающих этот протокол (например, VSCode). Чтобы включить отладку, используйте--inspectили--inspect-brkОпции для запуска Deno, соответствующие опции описываются следующим образом:

--inspect=<HOST:PORT>
  activate inspector on host:port (default: 127.0.0.1:9229)

--inspect-brk=<HOST:PORT>
  activate inspector on host:port and break at start of user script

--inspectопция позволяет подключить отладчик в любой момент, в то время как--inspect-brkПараметр будет ожидать подключения отладчика, приостанавливая выполнение на первой строке кода.

4.1 Chrome Devtools

Давайте отладим простую программу с помощью Chrome DevTools, мы будем использоватьstdизfile_server.ts, который представляет собой простой статический файл.

использовать--inspect-brkвозможность приостановить выполнение на первой строке кода.

$ deno run --inspect-brk --allow-read --allow-net https://deno.land/std@v0.50.0/http/file_server.ts
Debugger listening on ws://127.0.0.1:9229/ws/1e82c406-85a9-44ab-86b6-7341583480b1
Download https://deno.land/std@v0.50.0/http/file_server.ts
Compile https://deno.land/std@v0.50.0/http/file_server.ts
...

Открытьchrome://inspect, нажмите рядом с ЦельюInspect.

Для получения более подробных инструкций по отладке посетитеНет. слишком ленив для /manual/tool...URL-адрес.

4.2 VSCode

Deno можно отлаживать в VSCode. Официальная поддержка плагинов находится в стадии разработкиGitHub.com/Ноланд/против… launch.jsonНастройте, чтобы подключить отладчик:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Deno",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceFolder}",
      "runtimeExecutable": "deno",
      "runtimeArgs": ["run", "--inspect-brk", "-A", "<entry_point>"],
      "port": 9229
    }
  ]
}

Уведомление:Буду<entry_point>Замените на фактическое имя сценария.

Попробуем отладить локальный исходный файл, создадимserver.ts:

import { serve } from "https://deno.land/std@v0.50.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");

for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

Буду<entry_point>изменить наserver.ts, затем бегите.

debugger6

(Источник изображения:Нет. слишком ленив для /manual/tool...

debugger7

(Источник изображения:Нет. слишком ленив для /manual/tool...

Чтобы узнать больше о разнице между Deno и Node.js, вы можете прочитатьЧаоцзе_ Deno официально выпущен, досконально поймите разницу с Node.Эта статья.

Отличный обождый актуальные статьи

5. Знакомство с дубом

Я полагаю, что читатели, познакомившиеся с Node.js, знакомы с такими средами разработки веб-приложений, как Express, Hapi и Koa.Если вы также хотите разрабатывать веб-приложения на платформе Deno, вы можете рассмотреть возможность использования следующих готовых фреймворки напрямую:

  • deno-drash: микрофреймворк REST для Deno с нулевыми зависимостями.
  • deno-express: Способ Node Express для Deno.
  • oak: платформа промежуточного программного обеспечения для сетевого сервера Deno 🦕.
  • pogo: Серверная структура для Deno.
  • servest: 🌾Прогрессивный http-сервер для Deno🌾.

На момент написания этой статьи текущим проектом с наибольшим количеством Звезд был Дуб, плюс одна из моих Звезд, а это ровно 720. Кратко представимOak:

A middleware framework for Deno's http server, including a router middleware.

This middleware framework is inspired by Koa and middleware router inspired by koa-router.

Очевидно, что Oak вдохновлен Koa, а промежуточное ПО маршрутизации вдохновлено библиотекой koa-router. Если вы раньше использовали Koa, я думаю, вам будет легко начать работу с Oak. Если вы мне не верите, давайте рассмотрим пример:

import { Application } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

app.use((ctx) => {
  ctx.response.body = "Hello Semlinker!";
});

await app.listen({ port: 8000 });

Приведенный выше пример будет отвечать на каждый HTTP-запрос."Hello Semlinker!". Только один промежуточный слой кажется слишком простым, давайте рассмотрим более сложный пример (с использованием нескольких промежуточных слоев):

import { Application } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

// Logger
app.use(async (ctx, next) => {
  await next();
  const rt = ctx.response.headers.get("X-Response-Time");
  console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});

// Timing
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});

// Hello World!
app.use((ctx) => {
  ctx.response.body = "Hello World!";
});

await app.listen({ port: 8000 });

Чтобы лучше понять управление потоком промежуточного программного обеспечения Oak, давайте рассмотрим известную «луковую модель» Koa:

koa-onion-model

Из схемы примера «луковой модели» мы ясно видим, что запрос проходит через промежуточное ПО слой за слоем снаружи внутрь, а ответ проходит через промежуточное ПО слой за слоем изнутри наружу. После успешного выполнения приведенного выше кода мы открываем браузер и посещаемhttp://localhost:8000/URL-адрес, и в консоли будут выведены следующие результаты:

➜  learn-deno deno run --allow-net oak/oak-middlewares-demo.ts
GET http://localhost:8000/ - 0ms
GET http://localhost:8000/favicon.ico - 0ms

Что ж, после знакомства с базовым использованием Oak, давайте перейдем к сути, то есть к использованию Oak для разработки REST API.

6. Дуб боевой

В этой главе мы опишем, как использовать Oak для разработки Todo REST API, который поддерживает следующие функции:

  • Добавить новую задачу
  • Показать список дел
  • Получить детали указанного Todo
  • Удалить указанный Todo
  • Обновить указанную задачу

Ребята, вы готовы? Давайте шагнем в мир Дуба вместе!

6.1 Инициализация структуры проекта

Сначала мыlearn-denoВ проекте создайте новый каталог todos, затем создайте следующие подкаталоги и файлы TS соответственно:

  • каталог обработчиков:Сохраните процессор маршрутизации;
  • каталог промежуточного программного обеспечения:Хранить промежуточное ПО для обработки каждого запроса;
  • каталог моделей:Содержит определение модели, в нашем случае только интерфейс Todo;
  • каталог услуг:Хранить программы сервисного уровня;
  • каталог БД:В качестве локальной базы данных хранить данные Todo;
  • конфиг.тс:Содержит информацию о глобальной конфигурации приложения;
  • index.ts:Входной файл приложения;
  • маршрутизация.ts:Содержит информацию о маршрутизации API.

После завершения инициализации проекта структура каталогов проекта todos выглядит следующим образом:

└── todos
    ├── config.ts
    ├── db
    ├── handlers
    ├── index.ts
    ├── middlewares
    ├── models
    ├── routing.ts
    └── services

Как видите, эта структура каталогов выглядит как небольшое веб-приложение Node.js. Далее давайте создадим входной файл для проекта Todo.

6.2 Создать файл записи

index.ts

import { Application } from "https://deno.land/x/oak/mod.ts";
import { APP_HOST, APP_PORT } from "./config.ts";
import router from "./routing.ts";
import notFound from "./handlers/notFound.ts";
import errorMiddleware from "./middlewares/error.ts";

const app = new Application();

app.use(errorMiddleware);
app.use(router.routes());
app.use(router.allowedMethods());
app.use(notFound);

console.log(`Listening on ${APP_PORT}...`);

await app.listen(`${APP_HOST}:${APP_PORT}`);

В первой строке кода мы используем функциональность, предоставляемую Deno, которая заключается в импорте модулей непосредственно из сети. В остальном здесь ничего особенного. Мы создаем приложение, добавляем промежуточное ПО, маршрут и, наконец, запускаем сервер. Весь процесс похож на разработку обычного приложения Express/Koa.

6.3 Создать файл конфигурации

config.ts

const env = Deno.env.toObject();
export const APP_HOST = env.APP_HOST || "127.0.0.1";
export const APP_PORT = env.APP_PORT || 3000;
export const DB_PATH = env.DB_PATH || "./db/todos.json";

Чтобы повысить гибкость проекта, мы поддерживаем чтение информации о конфигурации из среды, а также предоставляем соответствующие значения по умолчанию для каждого элемента конфигурации. вDeno.env()Эквивалент платформы Node.jsprocess.env.

6.4 Добавить модель Todo

models/todo.ts

export interface Todo {
  id: number;
  userId: number;
  title: string;
  completed: boolean;
}

В модели задач мы определяем четыре свойства: идентификатор, идентификатор пользователя, заголовок и завершение, которые представляют номер задачи, номер пользователя, название задачи и статус завершения задачи соответственно.

6.5 Добавление маршрутов

routing.ts

import { Router } from "https://deno.land/x/oak/mod.ts";

import getTodos from "./handlers/getTodos.ts";
import getTodoDetail from "./handlers/getTodoDetail.ts";
import createTodo from "./handlers/createTodo.ts";
import updateTodo from "./handlers/updateTodo.ts";
import deleteTodo from "./handlers/deleteTodo.ts";

const router = new Router();

router
  .get("/todos", getTodos)
  .get("/todos/:id", getTodoDetail)
  .post("/todos", createTodo)
  .put("/todos/:id", updateTodo)
  .delete("/todos/:id", deleteTodo);

export default router;

Опять же ничего особенного, создаем роутер и добавляем маршруты. Это выглядит почти так же, как приложение Express.js.

6.6 Добавление обработчика маршрута

handlers/getTodos.ts

import { Response } from "https://deno.land/x/oak/mod.ts";
import { getTodos } from "../services/todos.ts";

export default async ({ response }: { response: Response }) => {
  response.body = await getTodos();
};

Обработчик getTodos используется для возврата всех задач. Если вы никогда не использовали Koa, объект ответа аналогичен объекту res в Express. В приложении Express мы вызываем метод json или send объекта res, чтобы вернуть ответ. В то время как в Koa/Oak нам нужно присвоить значение ответа свойству response.body.


handlers/getTodoDetail.ts

import { Response, RouteParams } from "https://deno.land/x/oak/mod.ts";
import { getTodo } from "../services/todos.ts";

export default async ({
  params,
  response,
}: {
  params: RouteParams;
  response: Response;
}) => {
  const todoId = params.id;

  if (!todoId) {
    response.status = 400;
    response.body = { msg: "Invalid todo id" };
    return;
  }

  const foundedTodo = await getTodo(todoId);
  if (!foundedTodo) {
    response.status = 404;
    response.body = { msg: `Todo with ID ${todoId} not found` };
    return;
  }

  response.body = foundedTodo;
};

Обработчик getTodoDetail используется для возврата задачи с указанным идентификатором., если задача, соответствующая указанному идентификатору, не найдена, будет возвращено 404 и соответствующее сообщение об ошибке.


handlers/createTodo.ts

import { Request, Response } from "https://deno.land/x/oak/mod.ts";
import { createTodo } from "../services/todos.ts";

export default async ({
  request,
  response,
}: {
  request: Request;
  response: Response;
}) => {
  if (!request.hasBody) {
    response.status = 400;
    response.body = { msg: "Invalid todo data" };
    return;
  }

  const {
    value: { userId, title, completed = false },
  } = await request.body();

  if (!userId || !title) {
    response.status = 422;
    response.body = {
      msg: "Incorrect todo data. userId and title are required",
    };
    return;
  }

  const todoId = await createTodo({ userId, title, completed });

  response.body = { msg: "Todo created", todoId };
};

Обработчик createTodo используется для создания нового Todo., прежде чем выполнять новую операцию, он проверит, отсутствует лиuserIdа такжеtitleНеобходимый.


handlers/updateTodo.ts

import { Request, Response } from "https://deno.land/x/oak/mod.ts";
import { updateTodo } from "../services/todos.ts";

export default async ({
  params,
  request,
  response,
}: {
  params: any;
  request: Request;
  response: Response;
}) => {
  const todoId = params.id;

  if (!todoId) {
    response.status = 400;
    response.body = { msg: "Invalid todo id" };
    return;
  }

  if (!request.hasBody) {
    response.status = 400;
    response.body = { msg: "Invalid todo data" };
    return;
  }

  const {
    value: { title, completed, userId },
  } = await request.body();

  await updateTodo(todoId, { userId, title, completed });

  response.body = { msg: "Todo updated" };
};

Обработчик updateTodo используется для обновления указанного Todo., перед выполнением обновления он определит, существует ли указанный Todo, и операция обновления будет выполнена только при его наличии.


handlers/deleteTodo.ts

import { Response, RouteParams } from "https://deno.land/x/oak/mod.ts";
import { deleteTodo, getTodo } from "../services/todos.ts";

export default async ({
  params,
  response
}: {
  params: RouteParams;
  response: Response;
}) => {
  const todoId = params.id;

  if (!todoId) {
    response.status = 400;
    response.body = { msg: "Invalid todo id" };
    return;
  }

  const foundTodo = await getTodo(todoId);
  if (!foundTodo) {
    response.status = 404;
    response.body = { msg: `Todo with ID ${todoId} not found` };
    return;
  }

  await deleteTodo(todoId);
  response.body = { msg: "Todo deleted" };
};

Обработчик deleteTodo используется для удаления указанного Todo., он проверит, пуст ли todoId и существует ли соответствующий Todo перед выполнением удаления.


В дополнение к уже определенным выше обработчикам нам также необходимо обрабатывать маршруты, которые не существуют и возвращают сообщение об ошибке.

handlers/notFound.ts

import { Response } from "https://deno.land/x/oak/mod.ts";

export default ({ response }: { response: Response }) => {
  response.status = 404;
  response.body = { msg: "Not Found" };
};

6.7 Добавление услуг

Перед созданием сервиса Todo создадим два небольшихhelper(вспомогательная) служба.

services/util.ts

import { v4 as uuid } from "https://deno.land/std/uuid/mod.ts";

export const createId = () => uuid.generate();

существуетutil.tsВ файле мы используем модуль uuid стандартной библиотеки Deno для создания уникального идентификатора для вновь созданного Todo.


services/db.ts

import { DB_PATH } from "../config.ts";
import { Todo } from "../models/todo.ts";

export const fetchData = async (): Promise<Todo[]> => {
  const data = await Deno.readFile(DB_PATH);

  const decoder = new TextDecoder();
  const decodedData = decoder.decode(data);

  return JSON.parse(decodedData);
};

export const persistData = async (data: Todo[]): Promise<void> => {
  const encoder = new TextEncoder();
  await Deno.writeFile(DB_PATH, encoder.encode(JSON.stringify(data)));
};

В нашем примереdb.tsФайл используется для управления данными, а метод сохранения данных использует локальный файл JSON. Чтобы получить все Todos, мыDB_PATHУстановите путь для прочтения соответствующего содержимого файла. Функция readfile возвращает объект uint8Array, который должен быть преобразован в строку перед анализами в объект JSON. Как Uint8Array, так и TextDecoder поступают из API Core JavaScript. Аналогично, при хранении данных необходимо сначала преобразовать строку в UINT8Array.

Чтобы все могли лучше понять содержание, изложенное выше, давайте взглянем на пространство имен Deno.readFileа такжеwriteFileОпределение этих двух методов:

1. Deno.readFile

 export function readFile(path: string): Promise<Uint8Array>;

Пример использования Deno.readFile:

const decoder = new TextDecoder("utf-8");
const data = await Deno.readFile("hello.txt");
console.log(decoder.decode(data));

2. Deno.writeFile

export function writeFile(
    path: string,
    data: Uint8Array,
    options?: WriteFileOptions
): Promise<void>;

Пример использования Deno.writeFile:

const encoder = new TextEncoder();
const data = encoder.encode("Hello world\n");
// overwrite "hello1.txt" or create it
await Deno.writeFile("hello1.txt", data);
// only works if "hello2.txt" exists
await Deno.writeFile("hello2.txt", data, {create: false});  
// set permissions on new file
await Deno.writeFile("hello3.txt", data, {mode: 0o777});  
// add data to the end of the file
await Deno.writeFile("hello4.txt", data, {append: true});  

Далее давайте определим основной сервис todos.ts, который используется для реализации добавления, удаления, модификации и проверки Todo.

services/todos.ts

import { fetchData, persistData } from "./db.ts";
import { Todo } from "../models/todo.ts";
import { createId } from "../services/util.ts";

type TodoData = Pick<Todo, "userId" | "title" | "completed">;

// 获取Todo列表
export const getTodos = async (): Promise<Todo[]> => {
  const todos = await fetchData();
  return todos.sort((a, b) => a.title.localeCompare(b.title));
};

// 获取Todo详情
export const getTodo = async (todoId: string): Promise<Todo | undefined> => {
  const todos = await fetchData();

  return todos.find(({ id }) => id === todoId);
};

// 新建Todo
export const createTodo = async (todoData: TodoData): Promise<string> => {
  const todos = await fetchData();

  const newTodo: Todo = {
    ...todoData,
    id: createId(),
  };

  await persistData([...todos, newTodo]);

  return newTodo.id;
};

// 更新Todo
export const updateTodo = async (
  todoId: string,
  todoData: TodoData
): Promise<void> => {
  const todo = await getTodo(todoId);

  if (!todo) {
    throw new Error("Todo not found");
  }

  const updatedTodo = {
    ...todo,
    ...todoData,
  };

  const todos = await fetchData();
  const filteredTodos = todos.filter((todo) => todo.id !== todoId);

  persistData([...filteredTodos, updatedTodo]);
};

// 删除Todo
export const deleteTodo = async (todoId: string): Promise<void> => {
  const todos = await getTodos();
  const filteredTodos = todos.filter((todo) => todo.id !== todoId);

  persistData(filteredTodos);
};

6.8 Добавление промежуточного ПО для обработки исключений

Что произойдет, если в пользовательском сервисе возникнет ошибка? Это может привести к сбою всего приложения. Чтобы этого избежать, мы можем добавить в каждый обработчикtry/catchblock, но на самом деле есть лучшее решение, заключающееся в добавлении промежуточного ПО для обработки исключений перед всеми маршрутами и внутри этого промежуточного ПО для перехвата всех исключений.

middlewares/error.ts

import { Response } from "https://deno.land/x/oak/mod.ts";

export default async (
  { response }: { response: Response },
  next: () => Promise<void>
) => {
  try {
    await next();
  } catch (err) {
    response.status = 500;
    response.body = { msg: err.message };
  }
};

6.9 Функциональная проверка

После разработки функции Todo мы можем использовать HTTP-клиент для тестирования интерфейса.Здесь я использую VSCode IDE.REST ClientДля расширения сначала создадим новый в корневом каталоге проекта.todo.httpфайл, затем скопируйте следующее:

### 获取Todo列表
GET http://localhost:3000/todos HTTP/1.1

### 获取Todo详情

GET http://localhost:3000/todos/${todoId}

### 新增Todo

POST http://localhost:3000/todos HTTP/1.1
content-type: application/json

{
    "userId": 666,
    "title": "Learn Deno"
}

### 更新Todo
PUT http://localhost:3000/todos/${todoId} HTTP/1.1
content-type: application/json

{
    "userId": 666,
    "title": "Learn Deno",
    "completed": true  
}

### 删除Todo
DELETE  http://localhost:3000/todos/${todoId} HTTP/1.1

дружеское напоминание:должен быть в курсеtodo.httpв файле${todoId}необходимо заменить фактическим номером задачи, который можно добавить, добавив сначала задачу, а затем изdb/todos.jsonполученный из файла.

Все на месте, мы просто обязаны ветру, следующий шаг - запустить наше приложение Todo, войти в корневую директорию проекта Todo, а затем запустить его в командной строкеdeno run -A index.tsЗаказ:

$ deno run -A index.ts
Listening on 3000...

в приведенной выше команде-Aзнак, с--allow-allФлаги эквивалентны и означают, что разрешены все разрешения.

-A, --allow-all
        Allow all permissions
        --allow-env
            Allow environment access
        --allow-hrtime
            Allow high resolution time measurement
        --allow-net=<allow-net>
            Allow network access
        --allow-plugin
            Allow loading plugins
        --allow-read=<allow-read>
            Allow file system read access
        --allow-run
            Allow running subprocesses
        --allow-write=<allow-write>
            Allow file system write access

Возможно, есть читатели, которые еще не использовали его.REST ClientРасширение, здесь я покажу, как добавить Todo:

deno-add-todo

Из возвращенного ответного сообщения HTTP мы можем узнатьLearn DenoЗадача успешно добавлена, давайте в целях безопасности откроем корневую директорию задачи.dbв каталогеtodos.jsonДокумент, убедитесь, что «склад» выполнен успешно, как показано на следующем рисунке:

todos-json

Это можно увидеть с рисункаLearn DenoTodo действительно был успешно добавлен, и читатели, интересующиеся другими интерфейсами, могут протестировать его самостоятельно.

Исходный код проекта Todo боя Deno:GitHub.com/Semelinker/The…

7. Справочные ресурсы