Skip to content

Bun HTTP 服务器

Bun 内置了高性能的 HTTP 服务器,使用简洁的 API 即可创建 Web 服务。本章介绍 Bun HTTP 服务器的使用方法。

基本服务器

最简示例

typescript
const server = Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response("Hello, Bun!");
  },
});

console.log(`服务器运行在 http://localhost:${server.port}`);

服务器配置

typescript
const server = Bun.serve({
  // 端口号
  port: 3000,
  
  // 主机地址
  hostname: "127.0.0.1",
  
  // 请求处理函数
  fetch(request, server) {
    return new Response("Hello!");
  },
  
  // 错误处理
  error(error) {
    console.error("服务器错误:", error);
    return new Response("服务器错误", { status: 500 });
  },
});

请求处理

Request 对象

typescript
Bun.serve({
  port: 3000,
  fetch(request) {
    // 请求方法
    console.log("方法:", request.method);
    
    // 请求 URL
    const url = new URL(request.url);
    console.log("路径:", url.pathname);
    console.log("查询参数:", url.searchParams);
    
    // 请求头
    console.log("Content-Type:", request.headers.get("content-type"));
    console.log("User-Agent:", request.headers.get("user-agent"));
    
    return new Response("OK");
  },
});

获取请求体

typescript
Bun.serve({
  port: 3000,
  async fetch(request) {
    // JSON 请求体
    if (request.headers.get("content-type")?.includes("application/json")) {
      const body = await request.json();
      console.log("JSON:", body);
    }
    
    // 文本请求体
    const text = await request.text();
    
    // FormData
    const formData = await request.formData();
    
    // ArrayBuffer
    const buffer = await request.arrayBuffer();
    
    return new Response("收到请求");
  },
});

表单数据

typescript
Bun.serve({
  port: 3000,
  async fetch(request) {
    if (request.method === "POST") {
      const formData = await request.formData();
      
      // 获取字段
      const name = formData.get("name");
      const email = formData.get("email");
      
      // 获取文件
      const file = formData.get("avatar") as File;
      if (file) {
        console.log("文件名:", file.name);
        console.log("文件大小:", file.size);
        
        // 保存文件
        await Bun.write(`./uploads/${file.name}`, file);
      }
      
      return Response.json({ name, email });
    }
    
    return new Response("请使用 POST 请求");
  },
});

响应处理

Response 对象

typescript
Bun.serve({
  port: 3000,
  fetch(request) {
    // 文本响应
    return new Response("Hello, World!");
    
    // 带状态码
    return new Response("未找到", { status: 404 });
    
    // 带响应头
    return new Response("Hello", {
      status: 200,
      headers: {
        "Content-Type": "text/plain; charset=utf-8",
        "X-Custom-Header": "custom-value",
      },
    });
  },
});

JSON 响应

typescript
Bun.serve({
  port: 3000,
  fetch(request) {
    const data = {
      message: "成功",
      timestamp: new Date().toISOString(),
      data: { id: 1, name: "Bun" },
    };
    
    // 使用 Response.json()
    return Response.json(data);
    
    // 带状态码
    return Response.json({ error: "未找到" }, { status: 404 });
    
    // 手动创建
    return new Response(JSON.stringify(data), {
      headers: { "Content-Type": "application/json" },
    });
  },
});

HTML 响应

typescript
Bun.serve({
  port: 3000,
  fetch(request) {
    const html = `
      <!DOCTYPE html>
      <html>
        <head><title>Bun 服务器</title></head>
        <body>
          <h1>欢迎使用 Bun!</h1>
          <p>当前时间: ${new Date().toLocaleString("zh-CN")}</p>
        </body>
      </html>
    `;
    
    return new Response(html, {
      headers: { "Content-Type": "text/html; charset=utf-8" },
    });
  },
});

文件响应

typescript
Bun.serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);
    
    // 返回静态文件
    if (url.pathname === "/image.png") {
      const file = Bun.file("./public/image.png");
      return new Response(file);
    }
    
    // 设置下载文件名
    if (url.pathname === "/download") {
      const file = Bun.file("./files/document.pdf");
      return new Response(file, {
        headers: {
          "Content-Disposition": 'attachment; filename="document.pdf"',
        },
      });
    }
    
    return new Response("Not Found", { status: 404 });
  },
});

路由处理

简单路由

typescript
Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    const path = url.pathname;
    const method = request.method;
    
    // 首页
    if (path === "/" && method === "GET") {
      return new Response("首页");
    }
    
    // API 路由
    if (path === "/api/users" && method === "GET") {
      return Response.json([{ id: 1, name: "张三" }]);
    }
    
    if (path === "/api/users" && method === "POST") {
      return Response.json({ message: "用户已创建" }, { status: 201 });
    }
    
    // 404
    return new Response("Not Found", { status: 404 });
  },
});

路由参数

typescript
Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    const path = url.pathname;
    
    // 匹配 /api/users/:id
    const userMatch = path.match(/^\/api\/users\/(\d+)$/);
    if (userMatch) {
      const userId = userMatch[1];
      return Response.json({ id: userId, name: `用户 ${userId}` });
    }
    
    // 匹配 /api/posts/:id/comments/:commentId
    const commentMatch = path.match(/^\/api\/posts\/(\d+)\/comments\/(\d+)$/);
    if (commentMatch) {
      const [, postId, commentId] = commentMatch;
      return Response.json({ postId, commentId });
    }
    
    return new Response("Not Found", { status: 404 });
  },
});

路由器类

typescript
// router.ts
type Handler = (request: Request, params: Record<string, string>) => Response | Promise<Response>;

interface Route {
  method: string;
  pattern: RegExp;
  paramNames: string[];
  handler: Handler;
}

class Router {
  private routes: Route[] = [];

  private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {
    const paramNames: string[] = [];
    const pattern = path.replace(/:(\w+)/g, (_, name) => {
      paramNames.push(name);
      return "([^/]+)";
    });
    return { pattern: new RegExp(`^${pattern}$`), paramNames };
  }

  add(method: string, path: string, handler: Handler) {
    const { pattern, paramNames } = this.pathToRegex(path);
    this.routes.push({ method, pattern, paramNames, handler });
  }

  get(path: string, handler: Handler) { this.add("GET", path, handler); }
  post(path: string, handler: Handler) { this.add("POST", path, handler); }
  put(path: string, handler: Handler) { this.add("PUT", path, handler); }
  delete(path: string, handler: Handler) { this.add("DELETE", path, handler); }

  async handle(request: Request): Promise<Response> {
    const url = new URL(request.url);
    
    for (const route of this.routes) {
      if (route.method !== request.method) continue;
      
      const match = url.pathname.match(route.pattern);
      if (match) {
        const params: Record<string, string> = {};
        route.paramNames.forEach((name, i) => {
          params[name] = match[i + 1];
        });
        return route.handler(request, params);
      }
    }
    
    return new Response("Not Found", { status: 404 });
  }
}

// 使用
const router = new Router();

router.get("/", () => new Response("首页"));
router.get("/api/users", () => Response.json([{ id: 1 }]));
router.get("/api/users/:id", (req, params) => 
  Response.json({ id: params.id })
);
router.post("/api/users", async (req) => {
  const body = await req.json();
  return Response.json({ created: body }, { status: 201 });
});

Bun.serve({
  port: 3000,
  fetch: (req) => router.handle(req),
});

中间件模式

实现中间件

typescript
type Middleware = (
  request: Request,
  next: () => Promise<Response>
) => Promise<Response>;

function compose(middlewares: Middleware[]) {
  return async (request: Request): Promise<Response> => {
    let index = 0;
    
    async function next(): Promise<Response> {
      if (index >= middlewares.length) {
        return new Response("Not Found", { status: 404 });
      }
      const middleware = middlewares[index++];
      return middleware(request, next);
    }
    
    return next();
  };
}

// 日志中间件
const logger: Middleware = async (req, next) => {
  const start = Date.now();
  const response = await next();
  console.log(`${req.method} ${new URL(req.url).pathname} - ${Date.now() - start}ms`);
  return response;
};

// CORS 中间件
const cors: Middleware = async (req, next) => {
  const response = await next();
  response.headers.set("Access-Control-Allow-Origin", "*");
  response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
  return response;
};

// 处理中间件
const handler: Middleware = async (req, next) => {
  return new Response("Hello!");
};

// 组合使用
const app = compose([logger, cors, handler]);

Bun.serve({
  port: 3000,
  fetch: app,
});

静态文件服务

简单静态服务器

typescript
import path from "node:path";

const PUBLIC_DIR = "./public";

Bun.serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);
    let filePath = path.join(PUBLIC_DIR, url.pathname);
    
    // 目录默认返回 index.html
    if (filePath.endsWith("/")) {
      filePath += "index.html";
    }
    
    const file = Bun.file(filePath);
    
    if (await file.exists()) {
      return new Response(file);
    }
    
    return new Response("Not Found", { status: 404 });
  },
});

MIME 类型处理

typescript
const MIME_TYPES: Record<string, string> = {
  ".html": "text/html",
  ".css": "text/css",
  ".js": "application/javascript",
  ".json": "application/json",
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".gif": "image/gif",
  ".svg": "image/svg+xml",
  ".ico": "image/x-icon",
  ".woff": "font/woff",
  ".woff2": "font/woff2",
};

function getMimeType(filePath: string): string {
  const ext = path.extname(filePath).toLowerCase();
  return MIME_TYPES[ext] || "application/octet-stream";
}

Bun.serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);
    const filePath = path.join("./public", url.pathname);
    const file = Bun.file(filePath);
    
    if (await file.exists()) {
      return new Response(file, {
        headers: { "Content-Type": getMimeType(filePath) },
      });
    }
    
    return new Response("Not Found", { status: 404 });
  },
});

HTTPS 服务器

TLS 配置

typescript
Bun.serve({
  port: 443,
  
  // TLS 证书
  tls: {
    key: Bun.file("./certs/key.pem"),
    cert: Bun.file("./certs/cert.pem"),
    
    // 可选:CA 证书
    // ca: Bun.file("./certs/ca.pem"),
    
    // 可选:密码短语
    // passphrase: "your-passphrase",
  },
  
  fetch(request) {
    return new Response("安全连接!");
  },
});

自签名证书

bash
# 生成自签名证书(开发用)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

服务器控制

停止服务器

typescript
const server = Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response("Hello!");
  },
});

// 停止服务器
server.stop();

// 或设置立即停止
server.stop(true);

重新加载

typescript
// 热重载支持
export default {
  port: 3000,
  fetch(request: Request) {
    return new Response("Hello!");
  },
};

// 运行: bun --hot server.ts

获取服务器信息

typescript
const server = Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response("Hello!");
  },
});

console.log("端口:", server.port);
console.log("主机:", server.hostname);
console.log("开发模式:", server.development);
console.log("待处理请求:", server.pendingRequests);

完整 REST API 示例

typescript
// api-server.ts
interface User {
  id: number;
  name: string;
  email: string;
}

const users: User[] = [
  { id: 1, name: "张三", email: "zhangsan@example.com" },
  { id: 2, name: "李四", email: "lisi@example.com" },
];

let nextId = 3;

const server = Bun.serve({
  port: 3000,
  
  async fetch(request) {
    const url = new URL(request.url);
    const path = url.pathname;
    const method = request.method;
    
    // CORS
    if (method === "OPTIONS") {
      return new Response(null, {
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
          "Access-Control-Allow-Headers": "Content-Type",
        },
      });
    }
    
    // GET /api/users
    if (path === "/api/users" && method === "GET") {
      return Response.json(users);
    }
    
    // GET /api/users/:id
    const getMatch = path.match(/^\/api\/users\/(\d+)$/);
    if (getMatch && method === "GET") {
      const user = users.find(u => u.id === parseInt(getMatch[1]));
      if (user) {
        return Response.json(user);
      }
      return Response.json({ error: "用户不存在" }, { status: 404 });
    }
    
    // POST /api/users
    if (path === "/api/users" && method === "POST") {
      const body = await request.json();
      const newUser: User = {
        id: nextId++,
        name: body.name,
        email: body.email,
      };
      users.push(newUser);
      return Response.json(newUser, { status: 201 });
    }
    
    // PUT /api/users/:id
    const putMatch = path.match(/^\/api\/users\/(\d+)$/);
    if (putMatch && method === "PUT") {
      const index = users.findIndex(u => u.id === parseInt(putMatch[1]));
      if (index !== -1) {
        const body = await request.json();
        users[index] = { ...users[index], ...body };
        return Response.json(users[index]);
      }
      return Response.json({ error: "用户不存在" }, { status: 404 });
    }
    
    // DELETE /api/users/:id
    const deleteMatch = path.match(/^\/api\/users\/(\d+)$/);
    if (deleteMatch && method === "DELETE") {
      const index = users.findIndex(u => u.id === parseInt(deleteMatch[1]));
      if (index !== -1) {
        users.splice(index, 1);
        return new Response(null, { status: 204 });
      }
      return Response.json({ error: "用户不存在" }, { status: 404 });
    }
    
    return Response.json({ error: "未找到" }, { status: 404 });
  },
  
  error(error) {
    console.error(error);
    return Response.json({ error: "服务器错误" }, { status: 500 });
  },
});

console.log(`API 服务器运行在 http://localhost:${server.port}`);

小结

本章介绍了:

  • ✅ 创建基本 HTTP 服务器
  • ✅ 处理请求和响应
  • ✅ 路由处理和参数解析
  • ✅ 中间件模式
  • ✅ 静态文件服务
  • ✅ HTTPS 配置
  • ✅ 完整 REST API 示例

下一步

继续阅读 WebSocket 了解 Bun 的实时通信功能。

本站内容仅供学习和研究使用。