跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何利用最常用继承写法在 Node.js 服务端高效构建控制器基类

首选 ES6 class + extends,配合 super() 显式调用父类构造函数;控制器基类应封装统一响应格式、请求上下文访问、基础校验、日志与追踪 ID 注入等共性能力,子类继承后重写具体业务方法,职责分离清晰,语义明确且与 Express/Koa 天然契合。 在 node . js 服务端开发中,用最常用、最稳妥的继承方式构建控制器基类,首选 es6 class + extends ,配合 super() 显式调用父类构造函数。这种方式语义清晰、调试友好、与现代框架(如 express、fastify)天然契合,且被所有主流 node.js 版本(v14+)原生支持,无需额外 polyfill 或 util.inherits 兼容层。 控制器基类应封装哪些共性能力 一个实用的控制器基类不是为了“炫技继承”,而是集中管理高频复用逻辑: 统一响应格式封装 :比如
success(data, msg)
、
fail(code, msg)
方法,避免每个路由手动拼 JSON 请求上下文快捷访问 :把
req
、
res
、
next
或 Koa 的
ctx
提前绑定或透传,减少参数重复传递 基础校验与错误预处理 :如自动解析 query/body、检查必填字段、捕获异步异常并委托给全局错误中间件 日志与追踪 ID 注入 :在构造时读取请求 ID(如
req.id
或
ctx.state.traceId
),供后续方法打点使用 标准写法:子类继承并重写具体方法 基类不直接处理路由,只提供可复用骨架;子类负责业务实现,保持职责分离:
// controllers/BaseController.js class BaseController { constructor(ctx) { this.ctx = ctx; this.logger = ctx.logger || console; }

success(data = null, msg = 'OK') { this.ctx.status = 200; this.ctx.body = { code: 0, msg, data }; }

fail(code = 500, msg = 'Server Error', data = null) { this.ctx.status = code; this.ctx.body = { code, msg, data }; }

// 可被子类覆盖的钩子 async beforeAction() {} async afterAction() {} }

module.exports = BaseController;

// controllers/UserController.js const BaseController = require('./BaseController');

class UserController extends BaseController { constructor(ctx) { super(ctx); // 必须调用,确保 this.ctx 正确初始化 }

async list() { await this.beforeAction(); try { const users = await db.query('SELECT * FROM users'); this.success(users); } catch (err) { this.fail(500, 'Query failed'); this.logger.error(err); } finally { await this.afterAction(); } }

async create() { const { name, email } = this.ctx.request.body; if (!name || !email) return this.fail(400, 'Missing required fields'); // ... 业务逻辑 } }

module.exports = UserController;

搭配路由注册的典型模式 Express 示例(推荐用箭头函数绑定 this,避免丢失上下文): php版微信js-sdk支付接口类 php版微信js-sdk支付接口类 下载
const UserController = require('./controllers/UserController');

router.get('/users', async (req, res, next) => { const ctrl = new UserController({ req, res, next, logger: req.app.logger }); await ctrl.list(); });

router.post('/users', async (req, res, next) => { const ctrl = new UserController({ req, res, next, logger: req.app.logger }); await ctrl.create(); });

Koa 更简洁,可直接传
ctx
:
router.get('/users', async (ctx) => { const ctrl = new UserController(ctx); await ctrl.list(); });
为什么不推荐 util.inherits? 虽然
util.inherits
是 Node.js 早期经典方案,但它存在明显局限: 仅复制原型链,无法继承构造函数内定义的实例属性和方法(如
this.logger
) 子类
constructor
与父类无自动关联,需手动调用
Parent.call(this, ...)
,易出错 无法使用
super.xxx()
,语法割裂,不利于 IDE 智能提示和类型推导 ES6 class 是当前标准,
util.inherits
已属于历史兼容方案,新项目无需回退

相关文章