如何在Mongoose的pre和post钩子中获取请求唯一标识?
解决Mongoose pre/post钩子关联请求的标识问题
核心问题原因
你遇到的$locals未定义问题,是因为最新版Mongoose的Query实例(查询钩子中的this)并没有内置$locals属性,该属性通常用于文档钩子(如save、remove)的上下文,而非查询操作的上下文。
可行解决方案
方案一:直接给Query实例添加自定义属性(TypeScript友好)
Mongoose允许直接给Query实例添加自定义属性,只需通过TypeScript扩展类型消除类型报错即可:
- 扩展Mongoose Query类型(创建类型声明文件,比如
src/types/mongoose.d.ts):
import 'mongoose'; declare module 'mongoose' { interface Query<ResultType, DocType, THelpers = {}, RawDocType = DocType> { requestId?: number; startTime?: number; } }
- 修改钩子代码:
let count = 0; UserSchema.pre(/.*/, async function() { this.requestId = count++; this.startTime = Date.now(); console.log(`Pre - Request id: ${this.requestId}`); }); UserSchema.post(/.*/, function() { const duration = Date.now() - this.startTime!; console.log(`Post - Request id: ${this.requestId}, 耗时: ${duration}ms`); });
方案二:使用Symbol避免属性命名冲突
如果担心自定义属性和Mongoose内置属性冲突,用Symbol作为属性键:
import 'mongoose'; const REQUEST_ID = Symbol('requestId'); const START_TIME = Symbol('startTime'); // 扩展类型 declare module 'mongoose' { interface Query<ResultType, DocType, THelpers = {}, RawDocType = DocType> { [REQUEST_ID]?: number; [START_TIME]?: number; } } let count = 0; UserSchema.pre(/.*/, async function() { this[REQUEST_ID] = count++; this[START_TIME] = Date.now(); console.log(`Pre - Request id: ${this[REQUEST_ID]}`); }); UserSchema.post(/.*/, function() { const duration = Date.now() - this[START_TIME]!; console.log(`Post - Request id: ${this[REQUEST_ID]}, 耗时: ${duration}ms`); });
方案三:结合NestJS请求上下文关联HTTP请求
如果需要将数据库查询与HTTP请求关联,用NestJS的AsyncLocalStorage传递请求ID:
- 创建异步存储工具:
// src/utils/async-local-storage.ts import { AsyncLocalStorage } from 'async_hooks'; export const asyncLocalStorage = new AsyncLocalStorage<{ requestId: string }>();
- 实现请求ID中间件:
// src/middlewares/request-id.middleware.ts import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { v4 as uuidv4 } from 'uuid'; import { asyncLocalStorage } from '../utils/async-local-storage'; @Injectable() export class RequestIdMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { const requestId = uuidv4(); asyncLocalStorage.run({ requestId }, next); } }
- 在钩子中获取请求ID:
import { asyncLocalStorage } from '../utils/async-local-storage'; UserSchema.pre(/.*/, async function() { const store = asyncLocalStorage.getStore(); const requestId = store?.requestId; this.startTime = Date.now(); console.log(`Pre - Request id: ${requestId}`); }); UserSchema.post(/.*/, function() { const store = asyncLocalStorage.getStore(); const requestId = store?.requestId; const duration = Date.now() - this.startTime!; console.log(`Post - Request id: ${requestId}, 耗时: ${duration}ms`); });
内容的提问来源于stack exchange,提问作者Roy Leibovitz
相关产品推荐
相关产品推荐

