Chapter 05

HTTP 服务器实战

从 Deno.serve() 原生 API 到 Hono 框架,构建现代 RESTful 服务

Deno.serve():原生 HTTP API

Deno 2 提供了简洁的内置 Deno.serve() API,基于 Web 标准的 Request/Response 模式,无需任何第三方框架即可构建 HTTP 服务器:

// server.ts — 最简单的 HTTP 服务器
Deno.serve((req: Request) => {
  return new Response("Hello, Deno 2!");
});

// 指定端口和主机
Deno.serve({ port: 8080, hostname: "0.0.0.0" }, (req) => {
  return new Response("Custom port");
});

// 带生命周期回调
Deno.serve({
  port: 8000,
  onListen({ hostname, port }) {
    console.log(`Server ready: http://${hostname}:${port}`);
  },
}, (req) => new Response("OK"));

手动路由

不依赖框架,用原生 URL 和条件判断实现路由:

const handler = async (req: Request): Promise<Response> => {
  const url = new URL(req.url);
  const { pathname } = url;
  const method = req.method;

  // GET /
  if (method === "GET" && pathname === "/") {
    return Response.json({ message: "Deno API", version: "2.0" });
  }

  // GET /api/users
  if (method === "GET" && pathname === "/api/users") {
    const page = Number(url.searchParams.get("page")) || 1;
    return Response.json({ page, users: [] });
  }

  // GET /api/users/:id(用正则提取参数)
  const userMatch = pathname.match(/^\/api\/users\/(\d+)$/);
  if (userMatch && method === "GET") {
    const id = Number(userMatch[1]);
    return Response.json({ id, name: "Alice" });
  }

  // POST /api/users
  if (method === "POST" && pathname === "/api/users") {
    const body = await req.json();
    return Response.json({ id: Date.now(), ...body }, { status: 201 });
  }

  return new Response("Not Found", { status: 404 });
};

Deno.serve(handler);

Hono 框架:推荐的 Deno 框架

Hono 是一个零依赖、超轻量的 Web 框架,原生支持 Deno、Node.js、Bun、Cloudflare Workers 等多种运行时。在 Deno 中使用 Hono 是目前最主流的选择:

// deno.json
{
  "imports": {
    "hono": "npm:hono@^4.0.0",
    "hono/middleware": "npm:hono/middleware"
  }
}
// app.ts — Hono 框架示例
import { Hono } from "hono";
import { cors } from "npm:hono/cors";
import { logger } from "npm:hono/logger";
import { zValidator } from "npm:@hono/zod-validator";
import { z } from "npm:zod";

const app = new Hono();

// 全局中间件
app.use("*", logger());
app.use("*", cors({ origin: "https://yourdomain.com" }));

// 路由定义
app.get("/", (c) => c.json({ message: "Deno + Hono" }));

// 路径参数
app.get("/users/:id", (c) => {
  const id = c.req.param("id");
  return c.json({ id, name: "Alice" });
});

// Zod 验证
const createUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

app.post("/users", zValidator("json", createUserSchema), (c) => {
  const data = c.req.valid("json");
  return c.json({ id: Date.now(), ...data }, 201);
});

// 错误处理
app.onError((err, c) => {
  console.error(err);
  return c.json({ error: err.message }, 500);
});

Deno.serve(app.fetch);

中间件模式

// 自定义中间件:JWT 认证
const authMiddleware = async (c: Context, next: Next) => {
  const auth = c.req.header("Authorization");
  if (!auth?..startsWith("Bearer ")) {
    return c.json({ error: "Unauthorized" }, 401);
  }
  // 验证 token...
  c.set("userId", "123");
  await next();
};

// 挂载到特定路由组
const api = app.basePath("/api");
api.use("*", authMiddleware);
api.get("/profile", (c) => {
  const userId = c.get("userId");
  return c.json({ userId });
});

WebSocket 服务器

Deno 原生支持 WebSocket 升级,无需任何第三方库:

// websocket.ts — 原生 WebSocket 聊天室
const clients = new Set<WebSocket>();

Deno.serve((req) => {
  // WebSocket 升级
  if (req.headers.get("upgrade") === "websocket") {
    const { socket, response } = Deno.upgradeWebSocket(req);

    socket.addEventListener("open", () => {
      clients.add(socket);
      console.log(`连接数: ${clients.size}`);
    });

    socket.addEventListener("message", ({ data }) => {
      // 广播给所有客户端
      for (const client of clients) {
        if (client.readyState === WebSocket.OPEN) {
          client.send(`[广播] ${data}`);
        }
      }
    });

    socket.addEventListener("close", () => {
      clients.delete(socket);
    });

    return response;
  }

  return new Response("Not a WebSocket request", { status: 400 });
});

实战:不依赖框架的 RESTful API

// rest-api.ts — 完整 CRUD API(无框架)
interface Todo { id: number; text: string; done: boolean; }

const todos = new Map<number, Todo>();
let nextId = 1;

function json(data: unknown, status = 200) {
  return Response.json(data, {
    status,
    headers: { "Access-Control-Allow-Origin": "*" },
  });
}

Deno.serve(async (req) => {
  const { pathname } = new URL(req.url);
  const idMatch = pathname.match(/^\/todos\/(\d+)$/);

  if (pathname === "/todos") {
    if (req.method === "GET") {
      return json([...todos.values()]);
    }
    if (req.method === "POST") {
      const { text } = await req.json();
      const todo: Todo = { id: nextId++, text, done: false };
      todos.set(todo.id, todo);
      return json(todo, 201);
    }
  }

  if (idMatch) {
    const id = Number(idMatch[1]);
    if (req.method === "GET") {
      const todo = todos.get(id);
      return todo ? json(todo) : json({ error: "Not found" }, 404);
    }
    if (req.method === "PATCH") {
      const todo = todos.get(id);
      if (!todo) return json({ error: "Not found" }, 404);
      const patch = await req.json();
      todos.set(id, { ...todo, ...patch });
      return json(todos.get(id));
    }
    if (req.method === "DELETE") {
      todos.delete(id);
      return new Response(null, { status: 204 });
    }
  }

  return json({ error: "Not Found" }, 404);
});

// deno run --allow-net rest-api.ts

本章小结Deno.serve() 基于 Web 标准 Request/Response,是 Deno 最简洁的 HTTP API。对于生产项目,推荐使用 Hono 框架——它轻量、类型安全、支持 Zod 验证,且与 Deno Deploy 完美配合。WebSocket 通过 Deno.upgradeWebSocket() 原生支持,无需第三方库。