从零开始实现一个 NestJS - 路由守卫

本系列的相关代码存放于 InkSha/expressive: 一个简易的仿造 Nest.js 的 NodeJS 后台框架。

本节,我们将实现路由守卫功能。

以下是一个用于进行 token 验证的守卫:

import { BadRequestException, Guard } from '@expressive/common'
import { Request } from 'express'

export class Auth extends Guard {

  // 发生在每个请求的中间件前
  public canContinue(request: Request): boolean {
    console.log('need auth')

    if (request?.headers?.token !== '123456') {
      throw new BadRequestException('need auth')
    }

    return true
  }
}

定义基类

定义一个抽象类 Guard ,后续所有的中间件都将基于它。

import type { Request } from 'express'

export abstract class Guard {
  public abstract canContinue(request: Request): boolean
}

定义装饰器

我们希望守卫可以应用在控制器和具体路由方法中。

@Controller("user")
@UseGuard(new Auth())
export class UserController {
  @UseGuard(new Auth())
  getProfile(){
    // ...
  }
}

因此我们需要增加一个装饰器。

// 将允许守卫应用于控制器和路由方法上
export type UseGuard = (guard: Guard) => ClassDecorator & MethodDecorator

export const UseGuard: UseGuard = (guard) => (target: Object, property?: string | symbol) => {
  const oldGuards: Guard[] = Reflect.getMetadata(TokenConfig.RouterGuard, target, property) || []
  Reflect.defineMetadata(TokenConfig.RouterGuard, oldGuards.concat(guard), target, property)
}

取出路由守卫

这一步将修改我们的 Router 类。

新增一个 applyGuard 方法。

// ...
  private applyGuard(name: string) {
    const baseGuards = (Reflect.getMetadata(TokenConfig.RouterGuard, this.controller) || []) as Array<Guard>
    const guards = baseGuards.concat((Reflect.getMetadata(TokenConfig.RouterGuard, this.controller, name) || []) as Array<Guard>)

    return (request: Request, response: Response, next: NextFunction) => {
      this.callHandle(() => {
        for (const guard of guards) {
          guard.canContinue(request)
        }
        return true
      })
        .then(data => {
          if (data === true) next()
          else {
            response.send(data)
          }
        })
    }
  }
// ...

接着,稍微修改 Router.bindRouter 方法。

// ...
      this.router[HttpRequestName[method]](url,
        // 这中间件之前调用路由守卫
        // 如果守卫验证失败,则会抛出一个错误
        // 错误已经在 applyGuard 中处理了,会直接返回响应
        this.applyGuard(name),
// ...

经过以上步骤,就可以完成基本的路由守卫功能了。


从零开始实现一个 NestJS - 路由守卫
http://www.inksha.com/archives/cong-ling-kai-shi-shi-xian-yi-ge-nestjs---lu-you-shou-wei
作者
inksha
发布于
2025年02月27日
许可协议