deno.com
本页内容

编写 HTTP 服务器

HTTP 服务器是 Web 的骨干,允许您访问网站、下载文件以及与 Web 服务交互。 它们监听来自客户端(如 Web 浏览器)的传入请求,并发送回响应。

当您构建自己的 HTTP 服务器时,您可以完全控制其行为,并可以根据您的特定需求进行定制。 您可以使用它进行本地开发,以提供您的 HTML、CSS 和 JS 文件,或构建 REST API - 拥有自己的服务器使您可以定义端点、处理请求和管理数据。

Deno 内置的 HTTP 服务器 跳转到标题

Deno 有一个内置的 HTTP 服务器 API,允许您编写 HTTP 服务器。 Deno.serve API 支持 HTTP/1.1 和 HTTP/2。

一个 “Hello World” 服务器 跳转到标题

Deno.serve 函数接受一个处理函数,该函数将为每个传入的请求调用,并期望返回一个响应(或一个解析为响应的 Promise)。

这是一个服务器示例,它为每个请求返回 “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 方法、标头、路径或 body 内容。

请求作为第一个参数传递给处理函数。 这是一个示例,展示了如何提取请求的各个部分

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!");
});

注意

请注意,如果用户在 body 完全接收之前挂断连接,则 req.text() 调用可能会失败。 请务必处理这种情况。 请注意,这可能发生在从请求 body 读取的所有方法中,例如 req.json()req.formData()req.arrayBuffer()req.body.getReader().read()req.body.pipeTo() 等。

使用真实数据响应 跳转到标题

大多数服务器不会对每个请求都响应 “Hello, World!”。 相反,它们可能会响应不同的标头、状态代码和 body 内容(甚至 body 流)。

这是一个返回 404 状态代码、JSON body 和自定义标头的响应示例

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",
    },
  });
});

使用流响应 跳转到标题

响应 body 也可以是流。 这是一个响应示例,它返回一个每秒重复 “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 函数。 当客户端挂断连接时会调用此函数。 重要的是要确保您处理这种情况,否则服务器将永远保持消息排队,并最终耗尽内存。

请注意,当客户端挂断连接时,响应 body 流将被“取消”。 请务必处理这种情况。 这可能会以 WritableStream 对象上的 write() 调用中的错误形式出现,该对象附加到响应 body ReadableStream 对象(例如通过 TransformStream)。

HTTPS 支持 跳转到标题

要使用 HTTPS,请在选项中传递两个额外的参数:certkey。 这些分别是证书和密钥文件的内容。

Deno.serve({
  port: 443,
  cert: Deno.readTextFileSync("./cert.pem"),
  key: Deno.readTextFileSync("./key.pem"),
}, handler);

注意

要使用 HTTPS,您将需要有效的 TLS 证书和服务器的私钥。

HTTP/2 支持 跳转到标题

当将 HTTP 服务器 API 与 Deno 结合使用时,HTTP/2 支持是“自动的”。 您只需要创建您的服务器,它将无缝处理 HTTP/1 或 HTTP/2 请求。

HTTP/2 也支持通过预先知识的明文。

自动 body 压缩 跳转到标题

HTTP 服务器内置了响应 body 的自动压缩。 当响应发送到客户端时,Deno 会确定响应 body 是否可以安全压缩。 这种压缩发生在 Deno 的内部,因此它快速而高效。

目前,Deno 支持 gzip 和 brotli 压缩。 如果满足以下条件,body 将自动压缩

  • 请求具有 Accept-Encoding 标头,该标头指示请求者支持 Brotli 的 brgzip。 Deno 将尊重标头中 质量值 的偏好。
  • 响应包含被认为是可压缩的 Content-Type。(该列表源自 jshttp/mime-db,实际列表在 代码中。)
  • 响应 body 大于 64 字节。

当响应 body 被压缩时,Deno 将设置 Content-Encoding 标头以反映编码,并确保调整或添加 Vary 标头以指示哪些请求标头影响了响应。

除了上面的逻辑之外,还有一些原因导致响应 不会 自动压缩

  • 响应包含 Content-Encoding 标头。 这表明您的服务器已经完成某种形式的编码。
  • 响应包含 Content-Range 标头。 这表明您的服务器正在响应范围请求,其中字节和范围是在 Deno 内部控制之外协商的。
  • 响应具有 Cache-Control 标头,其中包含 no-transform 值。 这表明您的服务器不希望 Deno 或任何下游代理修改响应。

服务 WebSockets 跳转到标题

Deno 可以将传入的 HTTP 请求升级为 WebSocket。 这允许您在 HTTP 服务器上处理 WebSocket 端点。

要将传入的 Request 升级为 WebSocket,您可以使用 Deno.upgradeWebSocket 函数。 这返回一个对象,该对象由 Response 和 Web 标准 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 流量。

注意

请注意,目前仅 HTTP/1.1 支持 WebSockets。

默认 fetch 导出 跳转到标题

在 Deno 中创建 HTTP 服务器的另一种方法是导出一个默认的 fetch 函数。 Fetch API 发起 HTTP 请求以从网络检索数据,并内置于 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/ 以查看 user-agent 信息。

在这些示例的基础上构建 跳转到标题

您可能希望扩展这些示例以创建更复杂的服务器。 Deno 推荐使用 Oak 来构建 Web 服务器。 Oak 是 Deno HTTP 服务器的中间件框架,旨在表达性强且易于使用。 它提供了一种使用中间件支持创建 Web 服务器的简单方法。 查看 Oak 文档 以获取有关如何定义路由的示例。

您找到所需的信息了吗?

隐私政策