Skip to content

NestJS 中间件使用场景

日志记录、JWT 认证、速率限制

日志记录

支持环境区分、JSON 格式输出、忽略健康检查接口

ts
// src/middlewares/logger.middleware.ts
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  private readonly logger = new Logger('HTTP');
  private readonly isProd: boolean;

  constructor(private readonly configService: ConfigService) {
    this.isProd = this.configService.get('NODE_ENV') === 'production';
  }

  use(req: Request, res: Response, next: NextFunction) {
    // 忽略健康检查接口
    if (req.originalUrl === '/health') {
      next();
      return;
    }

    const { method, originalUrl, ip } = req;
    const userAgent = req.get('user-agent') || '';
    const start = Date.now();

    res.on('finish', () => {
      const duration = Date.now() - start;
      const statusCode = res.statusCode;

      // 生产环境输出 JSON 格式,开发环境输出简易格式
      if (this.isProd) {
        this.logger.log(
          JSON.stringify({
            timestamp: new Date().toISOString(),
            method,
            url: originalUrl,
            statusCode,
            duration,
            ip,
            userAgent,
          })
        );
      } else {
        this.logger.log(
          `${method} ${originalUrl} ${statusCode} ${duration}ms - ${ip} ${userAgent}`
        );
      }
    });

    next();
  }
}

JWT 认证

结合 jsonwebtoken 实现接口认证

ts
// src/middlewares/jwt-auth.middleware.ts
import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import * as jwt from 'jsonwebtoken';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtAuthMiddleware implements NestMiddleware {
  private readonly secret: string;

  constructor(private readonly configService: ConfigService) {
    this.secret = this.configService.get('JWT_SECRET');
  }

  use(req: Request, res: Response, next: NextFunction) {
    try {
      const authHeader = req.headers.authorization;
      if (!authHeader || !authHeader.startsWith('Bearer ')) {
        throw new UnauthorizedException('Invalid authorization header');
      }

      const token = authHeader.split(' ')[1];
      const payload = jwt.verify(token, this.secret);
      req.user = payload; // 挂载用户信息到 req
      next();
    } catch (error) {
      throw new UnauthorizedException('Invalid or expired token');
    }
  }
}

速率限制

基于 rate-limiter-flexible 实现接口限流

ts
// src/middlewares/rate-limit.middleware.ts
import { Injectable, NestMiddleware, TooManyRequestsException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { RateLimiterMemory } from 'rate-limiter-flexible';

@Injectable()
export class RateLimitMiddleware implements NestMiddleware {
  private readonly limiter = new RateLimiterMemory({
    points: 10, // 10 次请求
    duration: 60, // 60 秒
  });

  async use(req: Request, res: Response, next: NextFunction) {
    const ip = req.ip;
    try {
      // 校验限流
      await this.limiter.consume(ip);
      next();
    } catch (error) {
      throw new TooManyRequestsException('Too many requests, please try again later');
    }
  }
}