Skip to content
字数
660 字
阅读时间
3 分钟

让我来详细解释 IP 限制功能的实现:

1 数据库设计:

在 MongoDB 中设计了两个相关的 Schema:

  • 问卷 Schema (question.schema.ts):
    @Prop({ default: 99 }) // 默认99次
ipLimit: number;
  • IP 记录 Schema (ip-record.schema.ts):
    @Schema({
  timestamps: true,
})
export class IpRecord {
  @Prop({ required: true })
  questionId: string;

  @Prop({ required: true })
  ip: string;

  @Prop({ required: true, default: 1 })
  count: number;
}

2 中间件实现 (ip-limit.middleware.ts):

    @Injectable()
export class IpLimitMiddleware implements NestMiddleware {
  async use(req: Request, res: Response, next: NextFunction) {
    const questionId = req.body.questionId;
    const ip = req.ip;

    // 1. 获取问卷信息和 IP 限制次数
    const question = await this.questionService.findOne(questionId);
    const ipLimit = question.ipLimit;

    // 2. 查找或创建 IP 记录
    let ipRecord = await this.ipRecordModel.findOne({ questionId, ip });

    // 3. 检查是否超过限制
    if (ipRecord && ipRecord.count >= ipLimit) {
      throw new HttpException('答题次数已达上限', HttpStatus.FORBIDDEN);
    }

    // 4. 更新访问次数
    await this.ipRecordModel.findOneAndUpdate(
      { questionId, ip },
      { $inc: { count: 1 } },
      { upsert: true, new: true },
    );

    next();
  }
}

3 前端设置界面 (PageSetting.tsx):

    <Form.Item label="问卷限制" name="ipLimit">
  <InputNumber min={0} />
</Form.Item>

4 后端 API 接口 (question.controller.ts):

    @Patch(':id/ip-limit')
async updateIpLimit(
  @Param('id') id: string,
  @Body('ipLimit') ipLimit: number,
  @Request() req,
) {
  const { username } = req.user;

  if (!ipLimit || ipLimit < 1) {
    throw new HttpException('IP限制次数必须大于0', HttpStatus.BAD_REQUEST);
  }

  return await this.questionService.update(id, { ipLimit }, username);
}

5 工作流程:

a) 设置阶段:

  • 问卷创建者可以在问卷设置中设置 IP 限制次数

  • 默认值为 99 次

  • 通过 API 将设置保存到数据库

b) 答卷阶段:

  • 用户提交问卷时,中间件会自动拦截请求

  • 获取用户 IP 和问卷 ID

  • 检查该 IP 的答题次数是否超过限制

  • 如果超过限制,返回 403 错误

  • 如果未超过限制,增加答题次数并继续处理请求

6 错误处理:

  • 当用户超过答题次数限制时,会收到 403 状态码

  • 错误响应格式:

 {
  "errno": -1,
  "message": "答题次数已达上限",
  "timestamp": "2024-05-01T12:00:00.000Z",
  "path": "/api/answer"
}

7 安全考虑:

  • 使用 Nginx 配置确保获取真实 IP

  • 使用中间件统一处理,避免绕过限制

  • 数据库层面使用事务确保数据一致性

这个功能的实现考虑了以下几个关键点:

  • 性能:使用 MongoDB 的 upsert 操作,减少查询次数

  • 安全性:中间件统一处理,不易绕过

  • 用户体验:友好的错误提示

  • 可配置性:问卷创建者可以自定义限制次数

  • 可靠性:使用数据库记录,保证数据持久化

贡献者

The avatar of contributor named as sunchengzhi sunchengzhi

文件历史

撰写