Bun HTTP 服务 #
概述 #
Bun 内置了高性能的 HTTP 服务器 Bun.serve,无需安装 Express 或 Fastify 等框架即可快速创建 Web 服务。
基础用法 #
最简服务器 #
typescript
Bun.serve({
port: 3000,
fetch(request) {
return new Response("Hello, Bun!");
},
});
console.log("Server running at http://localhost:3000");
返回 JSON #
typescript
Bun.serve({
port: 3000,
fetch(request) {
return Response.json({
message: "Hello, Bun!",
timestamp: Date.now(),
});
},
});
返回 HTML #
typescript
Bun.serve({
port: 3000,
fetch(request) {
return new Response(
`<!DOCTYPE html>
<html>
<head><title>Bun Server</title></head>
<body><h1>Hello, Bun!</h1></body>
</html>`,
{
headers: { "Content-Type": "text/html" },
}
);
},
});
请求处理 #
获取请求信息 #
typescript
Bun.serve({
port: 3000,
fetch(request) {
const url = new URL(request.url);
const info = {
method: request.method,
url: request.url,
pathname: url.pathname,
search: url.search,
headers: Object.fromEntries(request.headers),
};
return Response.json(info);
},
});
解析请求体 #
typescript
Bun.serve({
port: 300,
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/json" && request.method === "POST") {
const body = await request.json();
return Response.json({ received: body });
}
if (url.pathname === "/text" && request.method === "POST") {
const text = await request.text();
return new Response(`Received: ${text}`);
}
if (url.pathname === "/form" && request.method === "POST") {
const formData = await request.formData();
const name = formData.get("name");
return Response.json({ name });
}
return new Response("Not Found", { status: 404 });
},
});
解析查询参数 #
typescript
Bun.serve({
port: 3000,
fetch(request) {
const url = new URL(request.url);
const params = url.searchParams;
const page = params.get("page") || "1";
const limit = params.get("limit") || "10";
const sort = params.get("sort") || "created";
return Response.json({
page: parseInt(page),
limit: parseInt(limit),
sort,
});
},
});
路由 #
基础路由 #
typescript
const routes: Record<string, (req: Request) => Response | Promise<Response>> = {
"/": () => new Response("Home"),
"/about": () => new Response("About"),
"/api/status": () => Response.json({ status: "ok" }),
};
Bun.serve({
port: 3000,
fetch(request) {
const url = new URL(request.url);
const handler = routes[url.pathname];
if (handler) {
return handler(request);
}
return new Response("Not Found", { status: 404 });
},
});
RESTful 路由 #
typescript
interface User {
id: string;
name: string;
email: string;
}
const users: User[] = [
{ id: "1", name: "Alice", email: "alice@example.com" },
{ id: "2", name: "Bob", email: "bob@example.com" },
];
Bun.serve({
port: 3000,
async fetch(request) {
const url = new URL(request.url);
const method = request.method;
// GET /users
if (url.pathname === "/users" && method === "GET") {
return Response.json(users);
}
// GET /users/:id
const userMatch = url.pathname.match(/^\/users\/(\d+)$/);
if (userMatch && method === "GET") {
const user = users.find((u) => u.id === userMatch[1]);
if (user) {
return Response.json(user);
}
return new Response("User not found", { status: 404 });
}
// POST /users
if (url.pathname === "/users" && method === "POST") {
const body = (await request.json()) as Partial<User>;
const newUser: User = {
id: String(users.length + 1),
name: body.name || "",
email: body.email || "",
};
users.push(newUser);
return Response.json(newUser, { status: 201 });
}
// PUT /users/:id
const putMatch = url.pathname.match(/^\/users\/(\d+)$/);
if (putMatch && method === "PUT") {
const index = users.findIndex((u) => u.id === putMatch[1]);
if (index === -1) {
return new Response("User not found", { status: 404 });
}
const body = (await request.json()) as Partial<User>;
users[index] = { ...users[index], ...body };
return Response.json(users[index]);
}
// DELETE /users/:id
const deleteMatch = url.pathname.match(/^\/users\/(\d+)$/);
if (deleteMatch && method === "DELETE") {
const index = users.findIndex((u) => u.id === deleteMatch[1]);
if (index === -1) {
return new Response("User not found", { status: 404 });
}
users.splice(index, 1);
return new Response(null, { status: 204 });
}
return new Response("Not Found", { status: 404 });
},
});
路由器类 #
typescript
class Router {
private routes: Array<{
method: string;
pattern: RegExp;
handler: (req: Request, params: Record<string, string>) => Response | Promise<Response>;
}> = [];
get(pattern: string, handler: (req: Request, params: Record<string, string>) => Response | Promise<Response>) {
this.routes.push({ method: "GET", pattern: this.toRegExp(pattern), handler });
}
post(pattern: string, handler: (req: Request, params: Record<string, string>) => Response | Promise<Response>) {
this.routes.push({ method: "POST", pattern: this.toRegExp(pattern), handler });
}
put(pattern: string, handler: (req: Request, params: Record<string, string>) => Response | Promise<Response>) {
this.routes.push({ method: "PUT", pattern: this.toRegExp(pattern), handler });
}
delete(pattern: string, handler: (req: Request, params: Record<string, string>) => Response | Promise<Response>) {
this.routes.push({ method: "DELETE", pattern: this.toRegExp(pattern), handler });
}
private toRegExp(pattern: string): RegExp {
const paramNames: string[] = [];
const regexPattern = pattern.replace(/:([^/]+)/g, (_, name) => {
paramNames.push(name);
return "([^/]+)";
});
return new RegExp(`^${regexPattern}$`);
}
async handle(request: Request): Promise<Response> {
const url = new URL(request.url);
const pathname = url.pathname;
for (const route of this.routes) {
if (route.method !== request.method) continue;
const match = pathname.match(route.pattern);
if (match) {
const params = match.slice(1).reduce((acc, value, index) => {
acc[index.toString()] = value;
return acc;
}, {} as Record<string, string>);
return route.handler(request, params);
}
}
return new Response("Not Found", { status: 404 });
}
}
// 使用
const router = new Router();
router.get("/", () => new Response("Home"));
router.get("/users/:id", (req, params) => Response.json({ id: params["0"] }));
router.post("/users", async (req) => {
const body = await req.json();
return Response.json(body, { status: 201 });
});
Bun.serve({
port: 3000,
fetch: (req) => router.handle(req),
});
中间件 #
基础中间件 #
typescript
type Middleware = (
request: Request,
next: () => Promise<Response>
) => Promise<Response> | Response;
function createServer() {
const middlewares: Middleware[] = [];
function use(middleware: Middleware) {
middlewares.push(middleware);
}
async function handle(request: Request): Promise<Response> {
let index = 0;
async function next(): Promise<Response> {
if (index < middlewares.length) {
const middleware = middlewares[index++];
return middleware(request, next);
}
return new Response("Not Found", { status: 404 });
}
return next();
}
return { use, handle };
}
// 使用
const server = createServer();
// 日志中间件
server.use(async (request, next) => {
const start = Date.now();
const response = await next();
const duration = Date.now() - start;
console.log(`${request.method} ${request.url} - ${response.status} (${duration}ms)`);
return response;
});
// CORS 中间件
server.use(async (request, next) => {
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
},
});
}
const response = await next();
response.headers.set("Access-Control-Allow-Origin", "*");
return response;
});
// 认证中间件
server.use(async (request, next) => {
const url = new URL(request.url);
if (url.pathname.startsWith("/api/protected")) {
const auth = request.headers.get("Authorization");
if (!auth || !auth.startsWith("Bearer ")) {
return new Response("Unauthorized", { status: 401 });
}
}
return next();
});
Bun.serve({
port: 3000,
fetch: server.handle,
});
内置中间件集合 #
typescript
// 日志中间件
function logger() {
return async (request: Request, next: () => Promise<Response>) => {
const start = Date.now();
const response = await next();
console.log(
`[${new Date().toISOString()}] ${request.method} ${new URL(request.url).pathname} - ${response.status} (${Date.now() - start}ms)`
);
return response;
};
}
// CORS 中间件
function cors(options: {
origin?: string;
methods?: string[];
headers?: string[];
} = {}) {
const { origin = "*", methods = ["GET", "POST", "PUT", "DELETE"], headers = ["Content-Type"] } = options;
return async (request: Request, next: () => Promise<Response>) => {
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Methods": methods.join(", "),
"Access-Control-Allow-Headers": headers.join(", "),
},
});
}
const response = await next();
response.headers.set("Access-Control-Allow-Origin", origin);
return response;
};
}
// JSON 解析中间件
function jsonParser() {
return async (request: Request, next: () => Promise<Response>) => {
if (request.headers.get("Content-Type")?.includes("application/json")) {
try {
const body = await request.json();
(request as any).body = body;
} catch (e) {
return new Response("Invalid JSON", { status: 400 });
}
}
return next();
};
}
// 错误处理中间件
function errorHandler() {
return async (request: Request, next: () => Promise<Response>) => {
try {
return await next();
} catch (error) {
console.error("Error:", error);
return Response.json(
{ error: "Internal Server Error", message: String(error) },
{ status: 500 }
);
}
};
}
静态文件服务 #
基础静态服务 #
typescript
Bun.serve({
port: 3000,
async fetch(request) {
const url = new URL(request.url);
let pathname = url.pathname;
if (pathname === "/") {
pathname = "/index.html";
}
const filePath = `./public${pathname}`;
const file = Bun.file(filePath);
if (await file.exists()) {
return new Response(file);
}
return new Response("Not Found", { status: 404 });
},
});
高级静态服务 #
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",
".ttf": "font/ttf",
};
Bun.serve({
port: 3000,
async fetch(request) {
const url = new URL(request.url);
let pathname = url.pathname;
if (pathname === "/") {
pathname = "/index.html";
}
const filePath = `./public${pathname}`;
const file = Bun.file(filePath);
if (!(await file.exists())) {
// SPA 回退
const indexFile = Bun.file("./public/index.html");
if (await indexFile.exists()) {
return new Response(indexFile, {
headers: { "Content-Type": "text/html" },
});
}
return new Response("Not Found", { status: 404 });
}
const ext = pathname.substring(pathname.lastIndexOf("."));
const contentType = MIME_TYPES[ext] || "application/octet-stream";
return new Response(file, {
headers: {
"Content-Type": contentType,
"Cache-Control": "public, max-age=31536000",
},
});
},
});
WebSocket #
WebSocket 服务器 #
typescript
Bun.serve({
port: 3000,
fetch(request, server) {
const url = new URL(request.url);
if (url.pathname === "/ws") {
const success = server.upgrade(request);
if (success) {
return undefined;
}
return new Response("WebSocket upgrade failed", { status: 400 });
}
return new Response("Hello HTTP!");
},
websocket: {
open(ws) {
console.log("WebSocket opened");
ws.subscribe("chat");
},
message(ws, message) {
console.log("Received:", message);
ws.publish("chat", message);
},
close(ws) {
console.log("WebSocket closed");
ws.unsubscribe("chat");
},
},
});
console.log("Server running at http://localhost:3000");
广播消息 #
typescript
const clients = new Set<WebSocket>();
Bun.serve({
port: 3000,
fetch(request, server) {
if (server.upgrade(request)) {
return undefined;
}
return new Response("Hello");
},
websocket: {
open(ws) {
clients.add(ws as any);
console.log(`Client connected. Total: ${clients.size}`);
},
message(ws, message) {
for (const client of clients) {
if (client !== ws) {
client.send(message);
}
}
},
close(ws) {
clients.delete(ws as any);
console.log(`Client disconnected. Total: ${clients.size}`);
},
},
});
聊天室示例 #
typescript
interface ChatMessage {
type: "join" | "leave" | "message";
user: string;
content?: string;
timestamp: number;
}
const rooms = new Map<string, Set<WebSocket>>();
Bun.serve({
port: 3000,
fetch(request, server) {
const url = new URL(request.url);
if (url.pathname.startsWith("/chat/")) {
const roomId = url.pathname.replace("/chat/", "");
const success = server.upgrade(request, { data: { roomId } });
if (success) return undefined;
}
return new Response("Not Found", { status: 404 });
},
websocket: {
open(ws) {
const { roomId } = ws.data as { roomId: string };
if (!rooms.has(roomId)) {
rooms.set(roomId, new Set());
}
rooms.get(roomId)!.add(ws as any);
ws.subscribe(roomId);
ws.publish(roomId, JSON.stringify({
type: "join",
user: "Anonymous",
timestamp: Date.now(),
} as ChatMessage));
},
message(ws, message) {
const { roomId } = ws.data as { roomId: string };
const data = JSON.parse(message.toString());
ws.publish(roomId, JSON.stringify({
type: "message",
user: data.user,
content: data.content,
timestamp: Date.now(),
} as ChatMessage));
},
close(ws) {
const { roomId } = ws.data as { roomId: string };
rooms.get(roomId)?.delete(ws as any);
ws.unsubscribe(roomId);
},
},
});
SSL/TLS #
HTTPS 服务器 #
typescript
Bun.serve({
port: 443,
tls: {
cert: Bun.file("./cert.pem"),
key: Bun.file("./key.pem"),
},
fetch(request) {
return new Response("Hello, HTTPS!");
},
});
HTTP/2 #
typescript
Bun.serve({
port: 443,
tls: {
cert: Bun.file("./cert.pem"),
key: Bun.file("./key.pem"),
},
fetch(request) {
return new Response("Hello, HTTP/2!");
},
});
性能优化 #
连接配置 #
typescript
Bun.serve({
port: 3000,
fetch(request) {
return new Response("Hello!");
},
// 最大请求体大小
maxRequestBodySize: 1024 * 1024 * 10, // 10MB
// 开发模式
development: true,
// 错误处理
error(error) {
return new Response(`Error: ${error.message}`, { status: 500 });
},
});
响应缓存 #
typescript
const cache = new Map<string, { response: Response; expires: number }>();
Bun.serve({
port: 3000,
async fetch(request) {
const url = request.url;
const cached = cache.get(url);
if (cached && cached.expires > Date.now()) {
return cached.response;
}
const response = await generateResponse(request);
cache.set(url, {
response,
expires: Date.now() + 60000, // 1 分钟
});
return response;
},
});
下一步 #
现在你已经了解了 Bun 的 HTTP 服务,接下来学习 文件操作 深入了解 Bun 的文件系统 API。
最后更新:2026-03-29