You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Mongoose的pre和post钩子中获取请求唯一标识?

解决Mongoose pre/post钩子关联请求的标识问题

核心问题原因

你遇到的$locals未定义问题,是因为最新版Mongoose的Query实例(查询钩子中的this)并没有内置$locals属性,该属性通常用于文档钩子(如save、remove)的上下文,而非查询操作的上下文。

可行解决方案

方案一:直接给Query实例添加自定义属性(TypeScript友好)

Mongoose允许直接给Query实例添加自定义属性,只需通过TypeScript扩展类型消除类型报错即可:

  1. 扩展Mongoose Query类型(创建类型声明文件,比如src/types/mongoose.d.ts):
import 'mongoose';

declare module 'mongoose' {
  interface Query<ResultType, DocType, THelpers = {}, RawDocType = DocType> {
    requestId?: number;
    startTime?: number;
  }
}
  1. 修改钩子代码:
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:

  1. 创建异步存储工具:
// src/utils/async-local-storage.ts
import { AsyncLocalStorage } from 'async_hooks';

export const asyncLocalStorage = new AsyncLocalStorage<{ requestId: string }>();
  1. 实现请求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);
  }
}
  1. 在钩子中获取请求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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 17:46:00