守卫需要实现CanActivate接口
// guard/auth.guard.ts
@Injectable() // 守卫必须要Injectable装饰器修饰
export class AuthGuard implements CanActivate {
public canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
/**
* ExecutionContext : 继承ArgumentsHost接口,拥有着和过滤器一样的属性,并且多以下两个属性
* getClass()返回一个(控制器)类型(不是实例)。
* getHandler()将返回对服务的方法的引用
*/
const request: Request = context.switchToHttp().getRequest();
return true; //当返回false的时候,守卫不通过,直接抛出异常
}
}
import { Controller, UseGuards } from '@nestjs/common';
import { AuthGuard } from './guard/auth.guard'; // 使用守卫
@Controller('/app')
@UseGuards(new AuthGuard())
export class AppController { }
import { Controller, UseGuards, Get } from '@nestjs/common';
import { AuthGuard } from './guard/auth.guard'; // 使用守卫
@Controller('/app')
export class AppController {
@UseGuards(new AuthGuard())
@Get()
public getHello(): string {
return 'hello';
}
}
import { NestFactory } from '@nestjs/core';
import { AuthGuard } from './guard/auth.guard'; // 使用守卫
function async bootstrap(){
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
await app.listen(3000);
}
bootstrap();
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './guard/auth.guard'; // 使用守卫
@Module({
providers:[
{
provide: APP_GUARD, // APP_GUARD是一个常量,也可以自定义
useClass: AuthGuard,
},
]
})
export class ChildModule{ }
// guard/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Request } from 'express';
/**
* 我们希望根据分配给当前用户的角色与当前正在处理的路径所需的实际角色进行比较,使返回值成为条件。
* * 1.为了访问路径的角色(自定义元数据),我们将使用Reflector辅助类,它由框架提供并从@nestjs/core包中公开。
* * 2.在node.js世界中,通常的做法是将授权用户附加到request对象。因此,在上面的示例代码中,我们假设request.user包含用户实例和允许的角色。
* * 3.在您的应用程序中,您可能会在自定义身份验证防护(或中间件)中建立该关联。
* 当权限不足的用户请求端点时,Nest会自动返回以下响应:
* {
* "statusCode": 403,
* "message": "Forbidden resource"
* }
*/
@Injectable()
export class RolesGuard implements CanActivate {
public constructor(private readonly reflector: Reflector) { } // 注入Reflector到整个RolesGuard类中
public canActivate(context: ExecutionContext): boolean {
// 使用reflector的get方法,获取角色列表
// const roles = this.reflector.get('roles', context.getHandler());
// 我们可以通过提取控制器元数据并使用它来确定当前用户角色来使这个防护更通用。
// 要提取控制器元数据,我们通过context.getClass()而不是context.getHandler()
const roles = this.reflector.get<string[]>('roles', context.getClass());
// 也可以从数据库中获取角色列表,然后判断
if (!roles) {//当不存在角色列表的情况下,直接通过
return true;
}
const request: Request = context.switchToHttp().getRequest();
const user = (request as any).user; // 获取请求中的安全认证获取的用户信息 (比如jwt等,此处模拟)
const hasRole = () => user.roles.some((role) => roles.includes(role)); // 判断用户的角色是否包含和roles相同的角色列表,并返回一个布尔类型
return user && user.roles && hasRole(); // 当user存在 并且 有roles字段 然后判断roles是否包含在roles角色列表中
}
}
只是上面的角色列表纯属模拟,假的数据
拦截器实现NestInterceptor接口后,重写的intercept方法的参数如下:
1.第一个参数和guard的参数一致,ExecutionContext类型
2.第二个参数是调用句柄
2.1该CallHandler接口实现的handle()方法,你可以用它在你的拦截某些时候调用路由处理方法。
如果在handle()方法实现中没有调用intercept()方法,则根本不会执行路由处理程序方法。
// interceptor/logging.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
/**
* Aspect拦截 使用拦截器来记录用户交互(例如,存储用户调用,异步调度事件或计算时间戳)。
*/
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
/*
intercept()方法有效地包装请求/响应流。
*/
public intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');//开始执行到这个过滤器
const now = Date.now(); // 获取路由执行前的时间
return next
.handle() // 由于handle()返回RxJS Observable,我们可以使用多种运算符来操作流。在上面的例子中,我们使用了tap()运算符,它在可观察流的正常或异常终止时调用我们的匿名日志记录函数,但不会干扰响应周期。
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
// interceptor/excluedNull.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class ExcludeNullInterceptor implements NestInterceptor {
public intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(map(value => value === null ? '' : value ));
}
}
您想要处理路线请求的超时。当您的端点在一段时间后没有返回任何内容时,您希望以错误响应终止。以下结构可实现此目的
// interceptor/timeout.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { timeout } from 'rxjs/operators';
/**
* 您想要处理路线请求的超时。当您的端点在一段时间后没有返回任何内容时,您希望以错误响应终止。以下结构可实现此目的
*/
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
public intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(timeout(5000));
}
}
import { Controller, UseInterceptors } from '@nestjs/common';
import { TimeoutInterceptor } from './interceptor/timeout.interceptor'; // 使用超时拦截器
@Controller('/app')
@UseInterceptors(new TimeoutInterceptor())
export class AppController { }
import { Controller, UseInterceptors } from '@nestjs/common';
import { TimeoutInterceptor } from './interceptor/timeout.interceptor'; // 使用超时拦截器
@Controller('/app')
export class AppController {
@UseInterceptors(new TimeoutInterceptor())
@Get()
public getHello(): string {
return 'hello';
}
}
import { NestFactory } from '@nestjs/core';
import { TimeoutInterceptor } from './interceptor/timeout.interceptor'; // 使用超时拦截器
function async bootstrap(){
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new TimeoutInterceptor());
await app.listen(3000);
}
bootstrap();
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { TimeoutInterceptor } from './interceptor/timeout.interceptor'; // 使用超时拦截器
@Module({
providers:[
{
provide: APP_INTERCEPTOR, // APP_INTERCEPTOR是一个常量,也可以自定义
useClass: TimeoutInterceptor,
},
]
})
export class ChildModule{ }
req.user:为一个jwt安全认证之后存储在request的数据,如果没有,可以模拟
// decorator/user.decorator.ts
import { createParamDecorator } from '@nestjs/common'; // 引入创建参数装饰器
/**
* 原本我们是直接从request中获取到安全认证后的user,但是为了代码的可读性和透明性,我们可以创建一个装饰器并在所需要的路由中重复使用它
* data : 即传入的一个参数,正常模仿Body、Param等装饰器来说,data是传递字符串
*/
export const User = createParamDecorator((data: string, req: any) => {
/*
req.user = {
id: 'ca333ff2e2b2cea33',
username: 'name',
password: '123456'
}
*/
const value = data ? req.user[data] : req.user; // 当装饰器传递字符串参数的时候,我们可以获取到user中的一个属性,没有传递则直接返回整个user
return value;
});
//无参
interface UserType {
readonly id: string;
readonly username: string;
readonly password: string;
}
@Get() // 不带参数的情况下
async findOne(@User() user: UserType) {
console.log(`你这个用户的id为 ${id}`);
}
//带参
@Get() // 带参的时候
async findOne(@User('id') id: string) {
console.log(`你这个用户的id为 ${id}`);
}
import { Injectable, Inject, forwardRef } from '@nestjs/common';
// CommonService依赖注入了CatsService,而CatsService也依赖注入了CommonService
@Injectable()
export class CatsService {
public constructor(
@Inject(forwardRef(() => CommonService))
private readonly commonService: CommonService,
) { }
}
@Injectable()
export class CommonService {
public constructor(
@Inject(forwardRef(() => CatsService))
private readonly catsService: CatsService,
) { }
}
@Module({
imports: [forwardRef(() => CommonModule)],
})
export class CatsModule {}
@Module({
imports: [forwardRef(() => CatsModule)],
})
export class CommonModule {}
Nest提供了ModuleRef可以简单地注入任何组件的类。
import { Injectable, OnModuleInit } from '@nestjs/common';
import { CatsService } from './cats.service';
import { ModuleRef } from '@nestjs/core';
/**
* 3.Nest提供了ModuleRef可以简单地注入任何组件的类。也是解决循环依赖的方式
*/
@Injectable()
export class ModuleService implements OnModuleInit {
private service: CatsService;
public constructor(private readonly moduleRef: ModuleRef) { }
public onModuleInit() {
// ModuleRef有一个get()方法,允许检索当前模块中可用的提供程序。此外,您可以切换到非严格模式,这样您就可以在整个应用程序中选择任何现有的提供程序。
this.service = this.moduleRef.get(CatsService, { strict: false });
}
}
SINGLETON 每个提供程序可以跨多个类共享。提供者生命周期严格依赖于应用程序生命周期。应用程序引导后,所有提供程序都已实例化。默认情况下使用单例范围。
REQUEST 将在请求处理完成后为每个传入请求和垃圾专门创建提供程序的新实例。
TRANSIENT 瞬态提供者不能在提供者之间共享。每次当另一个提供程序向Nest容器请求特定的瞬态提供程序时,容器将创建一个新的专用实例。
import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {
constructor(@Inject(REQUEST) private readonly request: Request) { }
}
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT, // 加上这个属性
}
@Controller({
path: 'cats',
scope: Scope.REQUEST,
})
export class CatsController {}
scope作用域可以设置在服务类,自定义提供程序,控制器上,看情况而定
通过调用其构造函数创建注入/控制器后,(Nest在特定时刻按以下顺序调用生命周期钩子方法):
用法:每个生命周期钩子都由接口表示。接口在技术上是可选的,因为它们在TypeScript编译后无论如何都不存在。
import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
export class UsersService implements OnModuleInit {
onModuleInit() {
console.log(`The module has been initialized.`);
}
}
当然需要把UsersService注册到模块的providers中
其它三个钩子函数也是一样,只需要实现对应的生命周期接口,重写函数即可
但是使用OnModuleDestroy挂钩,需要在main.ts的app下面加入app.enableShutdownHooks();
才能使用
本博客主地址(单击跳转)