本页内容
编写 HTTP 服务器
HTTP 服务器是网络的支柱,它允许您访问网站、下载文件以及与 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)。
以下是一个服务器示例,它对每个请求返回一个“你好,世界!”响应
Deno.serve((_req) => {
return new Response("Hello, World!");
});
处理程序也可以返回一个 Promise<Response>
,这意味着它可以是一个 async
函数。
要运行此服务器,您可以使用 deno run
命令
deno run --allow-net server.ts
监听特定端口 跳转到标题
默认情况下,Deno.serve
将监听端口 8000
,但可以通过将端口号作为第一个或第二个参数传递到选项包中来更改此设置
// 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()
等。
用真实数据响应 跳转到标题
大多数服务器不会对每个请求都响应“你好,世界!”。相反,它们可能会响应不同的头部、状态码和正文内容(甚至正文流)。
以下是一个返回状态码 404、JSON 正文和自定义头部的响应示例
Deno.serve((req) => {
const body = JSON.stringify({ message: "NOT FOUND" });
return new Response(body, {
status: 404,
headers: {
"content-type": "application/json; charset=utf-8",
},
});
});
用流响应 跳转到标题
响应正文也可以是流。以下是一个响应示例,它返回一个每秒重复“你好,世界!”的流
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
函数。当客户端挂断连接时,会调用此函数。确保处理这种情况非常重要,否则服务器将永远排队消息,并最终耗尽内存。
请注意,当客户端挂断连接时,响应正文流会被“取消”。请务必处理这种情况。这可能会表现为附加到响应正文 ReadableStream
对象的 WritableStream
对象的 write()
调用中出现的错误(例如通过 TransformStream
)。
HTTPS 支持 跳转到标题
要使用 HTTPS,请在选项中传递两个额外参数:cert
和 key
。它们分别是证书文件和密钥文件的内容。
Deno.serve({
port: 443,
cert: Deno.readTextFileSync("./cert.pem"),
key: Deno.readTextFileSync("./key.pem"),
}, handler);
要使用 HTTPS,您的服务器需要一个有效的 TLS 证书和私钥。
HTTP/2 支持 跳转到标题
在使用 Deno 的 HTTP 服务器 API 时,HTTP/2 支持是“自动的”。您只需创建服务器,它将无缝处理 HTTP/1 或 HTTP/2 请求。
HTTP/2 也支持带有先验知识的明文传输。
自动正文压缩 跳转到标题
HTTP 服务器内置了响应正文的自动压缩功能。当响应发送给客户端时,Deno 会判断响应正文是否可以安全压缩。此压缩发生在 Deno 内部,因此快速高效。
目前 Deno 支持 gzip 和 brotli 压缩。如果满足以下条件,正文将自动压缩:
- 请求包含一个
Accept-Encoding
头部,表明请求者支持 Brotli 的br
或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。这使您可以在 HTTP 服务器上处理 WebSocket 端点。
要将传入的 Request
升级为 WebSocket,您可以使用 Deno.upgradeWebSocket
函数。此函数返回一个包含 Response
和 Web 标准 WebSocket
对象的对象。返回的响应应用于响应传入的请求。
由于 WebSocket 协议是对称的,WebSocket
对象与可用于客户端通信的对象相同。其文档可在 MDN 上找到。
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 导出 跳转到标题
在 Deno 中创建 HTTP 服务器的另一种方法是导出默认的 fetch
函数。fetch API 启动 HTTP 请求以从网络检索数据,并且内置于 Deno 运行时中。
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
服务器将启动并在控制台中显示一条消息。打开您的浏览器并导航到 https://:8000/ 以查看用户代理信息。
Deno.ServeDefaultExport
接口定义了可与 deno serve
命令一起使用的默认导出的结构。为确保您的代码正确进行类型检查,请务必将 satisfies Deno.ServeDefaultExport
添加到 export default { ... }
中。
基于这些示例进行构建 跳转到标题
您可能希望在这些示例的基础上构建更复杂的服务器。Deno 推荐使用 Oak 来构建 Web 服务器。Oak 是 Deno HTTP 服务器的中间件框架,旨在表达性强且易于使用。它提供了一种创建支持中间件的 Web 服务器的简单方法。请查阅 Oak 文档以获取如何定义路由的示例。