文章目录

Node.js 中间件:自定义中间件与错误处理

发布于 2026-04-02 19:12:01 · 浏览 6 次 · 评论 0 条

Node.js 中间件:自定义中间件与错误处理

在 Express 应用中,中间件是处理请求-响应周期的核心机制。创建自定义中间件能让你灵活控制请求流程,而正确处理错误则确保应用稳定运行。以下步骤将手把手教你实现这两项关键能力。


编写基础自定义中间件

  1. 新建一个 .js 文件(例如 logger.js),并在其中导出一个函数。该函数接收三个参数:req(请求对象)、res(响应对象)和 next(传递控制权的函数)。

    // logger.js
    function logger(req, res, next) {
      console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
      next(); // 必须调用 next() 才能继续后续处理
    }
    
    module.exports = logger;
  2. 在主应用文件(如 app.js)中引入这个中间件,并注册到 Express 实例。

    const express = require('express');
    const logger = require('./logger');
    
    const app = express();
    app.use(logger); // 对所有请求启用日志中间件
    
    app.get('/', (req, res) => {
      res.send('Hello World');
    });
    
    app.listen(3000);
  3. 启动应用后,每次访问任意路由,控制台都会打印带时间戳的请求日志。


编写带条件逻辑的中间件

中间件可以基于请求内容做判断,决定是否继续执行或提前终止。

  1. 创建一个验证 API 密钥的中间件 apiKeyValidator.js

    // apiKeyValidator.js
    function apiKeyValidator(req, res, next) {
      const apiKey = req.headers['x-api-key'];
      if (!apiKey || apiKey !== 'my-secret-key') {
        return res.status(401).json({ error: 'Invalid or missing API key' });
      }
      next(); // 验证通过,继续
    }
    
    module.exports = apiKeyValidator;
  2. 仅对特定路由组启用该中间件,避免影响公开接口:

    const express = require('express');
    const apiKeyValidator = require('./apiKeyValidator');
    
    const app = express();
    
    // 公开路由不受影响
    app.get('/public', (req, res) => {
      res.json({ message: 'This is public' });
    });
    
    // 受保护路由需验证密钥
    app.use('/api', apiKeyValidator);
    app.get('/api/data', (req, res) => {
      res.json({ data: 'Protected content' });
    });
  3. 测试时,访问 /api/data 若未提供正确 x-api-key 头,将收到 401 错误;而 /public 始终可访问。


实现统一错误处理中间件

Express 要求错误处理中间件必须显式接收四个参数err, req, res, next),即使不使用 next

  1. 编写全局错误处理器 errorHandler.js

    // errorHandler.js
    function errorHandler(err, req, res, next) {
      console.error(err.stack); // 记录错误堆栈
    
      // 开发环境返回详细错误,生产环境隐藏敏感信息
      const isDevelopment = process.env.NODE_ENV === 'development';
    
      res.status(err.status || 500).json({
        error: {
          message: err.message,
          ...(isDevelopment && { stack: err.stack })
        }
      });
    }
    
    module.exports = errorHandler;
  2. 在应用末尾注册此中间件(必须放在所有路由之后):

    const express = require('express');
    const errorHandler = require('./errorHandler');
    
    const app = express();
    
    // 模拟一个会抛出错误的路由
    app.get('/error', (req, res, next) => {
      const err = new Error('Something went wrong!');
      err.status = 500;
      next(err); // 通过 next(err) 触发错误处理
    });
    
    // 必须放在最后!
    app.use(errorHandler);
    
    app.listen(3000);
  3. 触发错误时(如访问 /error),客户端会收到结构化 JSON 响应,同时服务器记录完整错误堆栈。


处理异步操作中的错误

异步代码(如数据库查询)中的错误不会自动传递给 Express,需手动捕获。

  1. 封装一个辅助函数 asyncHandler,自动捕获 Promise 拒绝:

    // asyncHandler.js
    function asyncHandler(fn) {
      return (req, res, next) => {
        Promise.resolve(fn(req, res, next)).catch(next);
      };
    }
    
    module.exports = asyncHandler;
  2. 在路由中使用此包装器,避免重复写 try-catch:

    const express = require('express');
    const asyncHandler = require('./asyncHandler');
    
    const app = express();
    
    // 模拟异步操作失败
    app.get('/async-error', asyncHandler(async (req, res) => {
      throw new Error('Async operation failed!');
    }));
    
    app.use(require('./errorHandler')); // 复用之前的错误处理器
  3. 任何被拒绝的 Promise都会自动传递给错误处理中间件,无需手动调用 next(err)


中间件执行顺序与注意事项

中间件按注册顺序执行,错误处理中间件必须位于最后。以下是关键规则:

场景 正确做法 错误做法
提前终止请求 直接返回响应(如 res.send() 调用 next() 后又发送响应
传递错误 调用 next(err) 抛出未捕获异常(同步代码除外)
异步错误 asyncHandler 包装手动 catch 直接在 async 函数中抛错而不处理

始终确保每个中间件要么发送响应,要么调用 next(),否则请求会挂起直至超时。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文