FisherHub Blog
← 返回列表 | 工具笔记

Hono 中间件实战:Auth、CORS、Logger 全攻略

从认证到限流,用 Hono 内置中间件构建生产级 API 的完整方案

中间件链

Hono 的中间件模型类似 Koa,洋葱圈结构:

app.use('*', async (c, next) => {
  console.log('before');
  await next();
  console.log('after');
});

常用中间件组合

一个生产级 API 的中间件栈:

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { jwt } from 'hono/jwt';
import { timeout } from 'hono/timeout';
import { etag } from 'hono/etag';

const app = new Hono();

// 1. 全局 CORS
app.use('/api/*', cors({
  origin: ['https://blog.fisherhub.cn'],
  allowMethods: ['GET', 'POST'],
}));

// 2. 请求日志
app.use('*', logger());

// 3. 超时控制
app.use('/api/*', timeout(5000));

// 4. ETag 缓存
app.use('/api/*', etag());

// 5. 认证保护
app.use('/admin/*', jwt({ secret: Deno.env.get('JWT_SECRET')! }));

// 6. 公开 API
app.get('/api/posts', (c) => c.json({ posts: [] }));

// 7. 管理 API(需要 JWT)
app.get('/admin/stats', (c) => {
  const payload = c.get('jwtPayload');
  return c.json({ user: payload.sub, stats: {} });
});

自定义中间件

// 简单的 API Key 验证
const apiKey = (expectedKey: string) => {
  return async (c: any, next: any) => {
    const key = c.req.header('X-API-Key');
    if (key !== expectedKey) {
      return c.json({ error: 'Unauthorized' }, 401);
    }
    await next();
  };
};

app.use('/api/*', apiKey('my-secret-key'));

限流中间件

const rateLimit = (limit: number, windowMs: number) => {
  const store = new Map<string, { count: number; resetAt: number }>();

  return async (c: any, next: any) => {
    const ip = c.req.header('CF-Connecting-IP') || 'unknown';
    const now = Date.now();
    const record = store.get(ip);

    if (!record || now > record.resetAt) {
      store.set(ip, { count: 1, resetAt: now + windowMs });
      return next();
    }

    if (record.count >= limit) {
      return c.json({ error: 'Too many requests' }, 429);
    }

    record.count++;
    return next();
  };
};

app.use('/api/*', rateLimit(100, 60000)); // 每分钟 100 次

Hono 的中间件生态已经足够完善,大多数需求不需要自己从头写。