Spec-Zone.ru › Deno 2

Написание HTTP-сервера

HTTP-серверы являются основой веб-технологий, позволяя вам получать доступ к веб-сайтам, загружать файлы и взаимодействовать с веб-сервисами. Они слушают входящие запросы от клиентов (например, веб-браузеров) и отправляют ответы.

При создании собственного HTTP-сервера вы получаете полный контроль над его поведением и можете настроить его под свои конкретные потребности. Вы можете использовать его для локального разработки, для предоставления файлов HTML, CSS и JS, или для создания REST API — собственный сервер позволяет вам определять конечные точки, обрабатывать запросы и управлять данными.

Встроенный HTTP-сервер Deno

Deno предоставляет встроенный API HTTP-сервера, который позволяет вам писать HTTP-серверы. API Deno.serve поддерживает HTTP/1.1 и HTTP/2.

Сервер "Hello, World!"

Функция Deno.serve принимает функцию-обработчик, которая будет вызываться для каждого входящего запроса и должна возвращать ответ (или промис, разрешающий ответ).

Вот пример сервера, который возвращает ответ "Hello, World!" для каждого запроса:

server.ts
Deno.serve((_req) => {
  return new Response("Hello, World!");
});

Обработчик также может возвращать Promise<Response>, что означает, что он может быть async функцией.

Чтобы запустить этот сервер, вы можете использовать команду deno run:

deno run --allow-net server.ts

Прослушивание на определенном порту

По умолчанию Deno.serve будет прослушивать порт 8000, но это можно изменить, передав номер порта в пакете параметров в качестве первого или второго аргумента:

server.ts
// To listen on port 4242.
Deno.serve({ port: 4242 }, handler);

// To listen on port 4242 and bind to 0.0.0.0.
Deno.serve({ port: 4242, hostname: "0.0.0.0" }, handler);

Просмотр входящего запроса

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

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

Deno.serve(async (req) => {
  console.log("Method:", req.method);

  const url = new URL(req.url);
  console.log("Path:", url.pathname);
  console.log("Query parameters:", url.searchParams);

  console.log("Headers:", req.headers);

  if (req.body) {
    const body = await req.text();
    console.log("Body:", body);
  }

  return new Response("Hello, World!");
});
Внимание

Обратите внимание, что вызов req.text() может завершиться ошибкой, если пользователь прервет соединение до получения всего тела. Убедитесь, что вы обрабатываете этот случай. Обратите внимание, что это может произойти во всех методах, которые читают из тела запроса, таких как req.json(), req.formData(), req.arrayBuffer(), req.body.getReader().read(), req.body.pipeTo(), и т.д.

Отправка реальных данных в ответе

Большинство серверов не отвечают "Hello, World!" на каждый запрос. Вместо этого они могут возвращать разные заголовки, коды состояния и содержимое тела (даже потоки тела).

Вот пример возврата ответа со статусом 404, JSON-телом и пользовательским заголовком:

server.ts
Deno.serve((req) => {
  const body = JSON.stringify({ message: "NOT FOUND" });
  return new Response(body, {
    status: 404,
    headers: {
      "content-type": "application/json; charset=utf-8",
    },
  });
});

Отправка ответа в виде потока

Тело ответа также может быть потоком. Вот пример ответа, который возвращает поток "Hello, World!", повторяющийся каждую секунду:

server.ts
Deno.serve((req) => {
  let timer: number;
  const body = new ReadableStream({
    async start(controller) {
      timer = setInterval(() => {
        controller.enqueue("Hello, World!\n");
      }, 1000);
    },
    cancel() {
      clearInterval(timer);
    },
  });
  return new Response(body.pipeThrough(new TextEncoderStream()), {
    headers: {
      "content-type": "text/plain; charset=utf-8",
    },
  });
});
Примечание

Обратите внимание на функцию cancel выше. Она вызывается, когда клиент прерывает соединение. Важно убедиться, что вы обрабатываете этот случай, в противном случае сервер будет бесконечно накапливать сообщения и в конечном итоге исчерпает память.

Обратите внимание, что поток тела ответа «отменяется», когда клиент прерывает соединение. Убедитесь, что вы обрабатываете этот случай. Это может проявляться как ошибка в вызове write() на объекте WritableStream, который прикреплен к объекту тела ответа ReadableStream (например, через TransformStream).

Поддержка HTTPS

Для использования HTTPS передайте два дополнительных аргумента в параметры: cert и key. Это соответственно содержимое файлов сертификата и ключа.

Deno.serve({
  port: 443,
  cert: Deno.readTextFileSync("./cert.pem"),
  key: Deno.readTextFileSync("./key.pem"),
}, handler);
Примечание

Для использования HTTPS вам потребуется действительный TLS-сертификат и закрытый ключ для вашего сервера.

Поддержка HTTP/2

Поддержка HTTP/2 «автоматическая» при использовании API HTTP-сервера с Deno. Вам просто нужно создать свой сервер, и он будет обрабатывать запросы HTTP/1 или HTTP/2 без проблем.

HTTP/2 также поддерживается через cleartext при наличии предварительных знаний.

Автоматическое сжатие тела

HTTP-сервер имеет встроенное автоматическое сжатие тел ответов. При отправке ответа клиенту Deno определяет, можно ли безопасно сжать тело ответа. Это сжатие происходит внутри Deno, поэтому оно быстро и эффективно.

В настоящее время Deno поддерживает сжатие gzip и brotli. Тело автоматически сжимается, если выполняются следующие условия:

  • Запрос имеет заголовок Accept-Encoding, который указывает, что запрашивающий клиент поддерживает br для Brotli или gzip. Deno будет учитывать предпочтение значения качества в заголовке.
  • Ответ содержит Content-Type, который считается сжимаемым. (Список получен из jshttp/mime-db, а фактический список находится в коде.)
  • Тело ответа больше 64 байтов.

При сжатии тела ответа Deno установит заголовок Content-Encoding для отражения кодировки, а также позаботится о корректировке или добавлении заголовка Vary для указания, какие заголовки запроса повлияли на ответ.

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

  • Ответ содержит заголовок Content-Encoding. Это указывает, что ваш сервер уже выполнил некоторую форму кодирования.
  • Ответ содержит заголовок Content-Range. Это указывает, что ваш сервер отвечает на запрос диапазона, где байты и диапазоны согласовываются вне контроля Deno.
  • Ответ содержит заголовок Cache-Control, который содержит значение no-transform. Это указывает, что ваш сервер не хочет, чтобы Deno или какие-либо последующие прокси изменяли ответ.

Обработка WebSocket

Deno может повышать входящие HTTP-запросы до WebSocket. Это позволяет вам обрабатывать конечные точки WebSocket на ваших HTTP-серверах.

Чтобы повысить входящий Request до WebSocket, вы используете функцию Deno.upgradeWebSocket. Она возвращает объект, состоящий из Response и стандартного объекта WebSocket веб-стандартов. Возвращаемый ответ необходимо использовать для ответа на входящий запрос.

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

server.ts
Deno.serve((req) => {
  if (req.headers.get("upgrade") != "websocket") {
    return new Response(null, { status: 501 });
  }

  const { socket, response } = Deno.upgradeWebSocket(req);
  socket.addEventListener("open", () => {
    console.log("a client connected!");
  });

  socket.addEventListener("message", (event) => {
    if (event.data === "ping") {
      socket.send("pong");
    }
  });

  return response;
});

После повышения до WebSocket соединение, на котором был создан WebSocket, нельзя использовать для HTTP-трафика.

Примечание

Обратите внимание, что WebSocket в настоящее время поддерживаются только для HTTP/1.1.

Экспорт по умолчанию для fetch

Еще один способ создания HTTP-сервера в Deno — экспорт функции по умолчанию fetch. API fetch инициирует HTTP-запрос для получения данных через сеть и встроен в runtime Deno.

server.ts
export default {
  fetch(request) {
    const userAgent = request.headers.get("user-agent") || "Unknown";
    return new Response(`User Agent: ${userAgent}`);
  },
} satisfies Deno.ServeDefaultExport;

Вы можете запустить этот файл с помощью команды deno serve:

deno serve server.ts

Сервер запустится и выведет сообщение в консоль. Откройте браузер и перейдите по ссылке http://localhost:8000/, чтобы увидеть информацию о пользовательском агенте.

Разработка на основе этих примеров

Вероятно, вам захочется расширить эти примеры, чтобы создать более сложные серверы. Deno рекомендует использовать Oak для разработки веб-серверов. Oak — это фреймворк-средство для HTTP-сервера Deno, разработанный для выразительности и простоты использования. Он предоставляет простой способ создания веб-серверов с поддержкой middleware. Обратитесь к документации Oak для примеров определения маршрутов.

© 2018–2024 the Deno authors
Licensed under the MIT License.
https://docs.deno.com/runtime/fundamentals/http_server

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API