JS静态类如何调用静态方法?Express控制器报错解决
报错核心原因
- Express 路由注册控制器方法时,类实例方法的
this上下文默认会丢失,直接调用router.xxx('/path', controller.add)时,方法内部的this会变成undefined,自然无法读取getRequestValues方法。 - 原代码存在多处逻辑缺陷:
- 调用异步方法
getRequestValues时没有加await,会导致异步逻辑还没执行完就走后续判断 delete方法调用getRequestValues时没有传入req参数,根本拿不到请求数据- 使用类静态字段存储请求级数据存在严重并发问题:Node.js 单线程模型下,多个同时进来的请求会互相覆盖静态字段的值,导致A用户的操作改到B用户的购物车数据
- 部分变量引用缺少
this前缀(比如basket.id直接写basket,会触发未定义报错) - 删除商品时查询条件只传了
deviceId,会跨用户删除其他人购物车的同ID商品
- 调用异步方法
符合DRY原则的正确实现
不要用静态字段/实例属性存储请求级数据,抽离的公共方法直接返回解析后的上下文数据,同时做好this绑定即可。
修复后的控制器代码
const {Basket, BasketDevice, Device} = require('../models/models') const ApiError = require('../error/ApiError') class BasketController { // 抽离公共解析逻辑,纯异步方法直接返回结果,不依赖全局/类级存储 async getRequestData(req) { const {deviceId, quantity = 1} = req.body const basket = await Basket.findOne({ where: {userId: req.user.id}, }) const basketDevice = await BasketDevice.findOne({ where: {deviceId, basketId: basket.id}, }) return { deviceId, quantity: Number(quantity), basket, basketDevice } } async add(req, res, next){ const {deviceId, quantity, basket, basketDevice} = await this.getRequestData(req) if(basketDevice){ await BasketDevice.update( {quantity: basketDevice.quantity + quantity}, {where: {deviceId, basketId: basket.id}} ) } else { await BasketDevice.create({deviceId, basketId: basket.id, quantity}) } return res.json({message: `Successfully added ${quantity} units of goods`}) } async getOne(req, res, next){ const basket = await Basket.findOne({ where: {userId: req.user.id}, include: { model: BasketDevice, include: { model: Device }, attributes: ['id', 'deviceId', 'basketId', 'quantity'], }, }) return res.json(basket) } async delete(req, res, next){ const {deviceId, quantity, basket, basketDevice} = await this.getRequestData(req) if(!basketDevice){ return next(ApiError.badRequest('The product in the basket is already missing')) } if(basketDevice.quantity <= quantity){ await BasketDevice.destroy({ where: { deviceId, basketId: basket.id } }) return res.json({message: 'The product was successfully deleted'}) } await BasketDevice.update( {quantity: basketDevice.quantity - quantity}, {where: {deviceId, basketId: basket.id}} ) return res.json({message: `Successfully deleted ${quantity} units of goods`}) } } module.exports = new BasketController()
路由层写法修正
注册路由时必须绑定控制器实例的this上下文,二选一即可:
const basketController = require('./controllers/basketController') // 写法1:箭头函数包裹(更直观,推荐) router.post('/basket/add', (req, res, next) => basketController.add(req, res, next)) router.get('/basket', (req, res, next) => basketController.getOne(req, res, next)) router.delete('/basket/delete', (req, res, next) => basketController.delete(req, res, next)) // 写法2:手动bind this // router.post('/basket/add', basketController.add.bind(basketController))
额外修复的原有逻辑问题
- 新增商品首次加入购物车时默认给
quantity=1,避免数据库存null值 - 删除商品时补全
basketId查询条件,避免越权删除其他用户的购物车数据 - 删除数量大于等于商品现有数量时直接删除整条记录,避免购物车商品数量出现负数
- 所有数据库异步操作都补全
await,避免时序错误 - 修正了返回文案的拼写错误
内容的提问来源于stack exchange,提问作者rjunovskii
相关产品推荐
相关产品推荐

