请求上下文

简介: 请求上下文

需求

随着系统的复杂性增加,我们很容易遇到一种场景,就是想要知道当前进行资源操作的究竟是谁。比如,系统中有超过一个用户拥有删除商品的权限,某一天突然发现有个商品被删除了(我们系统全部是软删除,这里以软删除举例),想要知道删除这个商品的究竟是谁。再比如,系统中有超过一个用户拥有发送系统通知的权限,追溯某一条通知是谁发的也是一样的道理。


上面这些案例如果系统日志和用户活动日志做的足够好的话,是可以达到目的的。但是有一些场景,系统日志和用户活动日志就无能为力了,比如本文所介绍的 RBAC 扩展场景。


方案调研

由于此部分篇幅过长,抽离到独立笔记:方案调研


方案实现

不要跳过上一步直接看这个!根据需求来看是否需要这种方案!


Step1. 安装依赖

$ yarn add cls-hooked
$ yarn add -D @types/cls-hooked点击复制复制失败已复制


Step2. 构造请求上下文类—— RequestContext

request.context.ts 文件中写入如下内容:

import { getNamespace } from 'cls-hooked';
import { Request, Response } from 'express';
import { v4 as uuid } from 'uuid';
import { JwtPayload } from '../../user/interfaces/jwt-payload.interface';
export class RequestContext {
  public static nsid = uuid();
  public uuid: string;
  public request: Request;
  public response: Response;
  /**
   * 请求上下文构造函数
   * @param request 请求对象
   * @param response 返回对象
   */
  constructor(request: Request, response: Response) {
    this.uuid = uuid();
    this.request = request;
    this.response = response;
  }
  /**
   * 获取请求上下文
   * @returns 当前请求上下文
   */
  public static currentRequestContext(): RequestContext {
    const namespace = getNamespace(RequestContext.nsid);
    return namespace && namespace.active ? namespace.get(RequestContext.name) : null;
  }
  /**
   * 获取当前请求
   * @returns 当前请求
   */
  public static currentRequest(): Request {
    const requestContext = RequestContext.currentRequestContext();
    return requestContext?.request;
  }
  /**
   * 获取当前用户
   * @returns 当前用户
   */
  public static currentUser(): JwtPayload {
    const requestContext = RequestContext.currentRequestContext();
    return requestContext?.request?.user;
  }
}点击复制复制失败已复制


提示

项目采用 jwt 的方式进行校验,将用户信息解密后挂载到 Request 请求中,所以在 types.d.ts 中扩展了 express 中的 Request 对象。当前操作用户可以通过 express.Request.user 来拿到。


Step3. 编写中间件—— RequestContextMiddleware

request-context.middleware.ts 文件中写入如下内容:

import { NextFunction, Response, Request } from 'express';
import { RequestContext } from '../context/request.context';
import { getNamespace, createNamespace } from 'cls-hooked';
/**
 * 请求上下文中间件
 * @param req 请求参数
 * @param res 返回参数
 * @param next next函数
 */
export function RequestContextMiddleware(req: Request, res: Response, next: NextFunction) {
  const requestContext = new RequestContext(req, res);
  const namespace = getNamespace(RequestContext.nsid) || createNamespace(RequestContext.nsid);
  namespace.run(() => {
    namespace.set(RequestContext.name, requestContext);
    next();
  });
}点击复制复制失败已复制


Step4. 启用中间件

最简单的是全局启用中间件,在 app.module.ts 中进行如下配置:

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RequestContextMiddleware).forRoutes({ path: '*', method: RequestMethod.ALL });
  }
}点击复制复制失败已复制


提示

启用这个中间件会损耗性能,全局启用并不合适,应该在需要的地方单独配置!比如,我们的场景在查询时不需要上下文功能,完全没有必要用 RequestMethod.ALL ,具体如何配置请结合自己的实际场景进行思考。

目录
相关文章
|
Java 测试技术 API
【SpringMVC】参数传递与用户请求和响应(上)
【SpringMVC】参数传递与用户请求和响应(上)
68 0
|
2月前
|
机器学习/深度学习 自然语言处理
上下文无关与上下文相关
上下文无关与上下文相关
|
5月前
|
JSON 前端开发 JavaScript
关于我认识的请求方式
关于我认识的请求方式有三个
55 0
|
12月前
|
C#
C# 当前上下文中不存在InitializeComponent()
C#——当前上下文中不存在InitializeComponent()可能原因是:项目文件直接由外部加载进来时可能出现错误。可以先检查xaml文件的开头x:Class=“day27test02.MainWindow”是否是正确的类名。如果不是,改成对应的项目的类即可。这是本人碰到的这种情况通过这种方式得到解决的,仅供参考。
633 1
C# 当前上下文中不存在InitializeComponent()
|
10月前
全局响应返回处理
全局响应返回处理
33 0
|
12月前
|
存储 Linux 调度
上下文之->解密篇
上下文之->解密篇
37 0
|
JSON 前端开发 Java
【SpringMVC】参数传递与用户请求和响应(下)
【SpringMVC】参数传递与用户请求和响应(下)
68 0
ResponseBodyAdvice还可以用于处理控制器抛出异常时的响应格式
除了对控制器返回数据进行统一的处理外,ResponseBodyAdvice还可以用于处理控制器抛出异常时的响应格式。以下是一个示例,实现了对异常的包装,添加了响应码和响应消息:
servlet中请求对象与响应对象的生命周期
servlet中请求对象与响应对象的生命周期
105 0
servlet中请求对象与响应对象的生命周期
|
前端开发
[SpringMVC]请求与响应②(五种类型参数传递)
请求与响应②(五种类型参数传递)
[SpringMVC]请求与响应②(五种类型参数传递)