快速入门 Nestjs(二)
RxJs 与 Nestjs
RxJS(Reactive Extensions for JavaScript)是一个用于异步编程的库,它基于可观察对象(Observables)来处理异步事件和数据流。RxJS 提供了强大的操作符来处理和转换这些数据流,使得代码更加简洁和可维护。RxJS 的核心理念是响应式编程,它强调数据流和变化传播
基本概念
- Observable: 可观察对象,用于表示一个异步数据流
- Observer: 观察者,对可观察对象发出的数据进行处理
- Subscription: 订阅,通过订阅可以开始接收可观察对象发出的数据
- Operators: 操作符,用于对数据流进行转换、过滤等操作
- Subject: 特殊类型的可观察对象,可以同时作为数据生产者和消费者
以下是一个简单的 RxJS 示例,用于演示如何创建一个 Observable 并订阅它
import { Observable } from 'rxjs';
const observable = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.complete();
});
observable.subscribe({
next(x) { console.log('Received value: ' + x); },
error(err) { console.error('Error: ' + err); },
complete() { console.log('Complete'); }
});
// Received value: 1
// Received value: 2
// Received value: 3
// Complete
在 NestJS 中使用 RxJS
- NestJS 深度集成了 RxJS,使得处理异步操作更加方便
import { Injectable } from '@nestjs/common';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class ListService {
findAll(): Observable<string[]> {
const cats = ['Tom', 'Jerry', 'Luxi'];
return of(cats);
}
findOne(): Observable<string[]> {
const cats = ['Tom', 'Jerry', 'Jack'];
return of(cats).pipe(
map((catsArray) => catsArray.map((cat) => cat.toUpperCase())),
);
}
}
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { ListService } from './list.service';
import { Observable } from 'rxjs';
@Controller('list')
export class ListController {
constructor(private readonly listService: ListService) {}
@Get()
findAll(): Observable<string[]> {
return this.listService.findAll();
}
@Post()
findOne(): Observable<string[]> {
return this.listService.findOne();
}
}
- RxJS 是一个功能强大的库,有很多方法,用于处理异步事件和数据流。
Nestjs 响应拦截器
-
拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:
- 在函数执行之前/之后绑定额外的逻辑
- 转换从函数返回的结果
- 转换从函数抛出的异常
- 扩展基本函数行为
- 根据所选条件完全重写函数 (例如, 缓存目的)
-
创建拦截器的基本步骤:需要创建一个类,它实现了 nest.js 提供的
NestInterceptor
接口
// 创建一个ts文件,然后在main.ts文件引入
import { Injectable, NestInterceptor, CallHandler } from '@nestjs/common';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
interface data<T> {
data: T;
}
@Injectable()
export class Response<T = any> implements NestInterceptor {
intercept(context, next: CallHandler): Observable<data<T>> {
// 使用了`rxjs`的`pipe`方法和`map`操作符来转换返回的数据
return next.handle().pipe(
map((data) => {
return {
data,
status: 0,
success: true,
message: '牛逼',
};
}),
);
}
}
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Response } from './common/response';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new Response());
await app.listen(3000);
}
bootstrap();
Nestjs 异常过滤器
异常过滤器是用于捕获和处理抛出的异常,并生成适当的 HTTP 响应的类。通过使用异常过滤器,你可以将异常处理逻辑从控制器或服务中分离出来
import { ExceptionFilter, Catch, ArgumentsHost,HttpException } from '@nestjs/common'
import {Request,Response} from 'express'
@Catch(HttpException)
export class HttpFilter implements ExceptionFilter {
catch(exception:HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const request = ctx.getRequest<Request>()
const response = ctx.getResponse<Response>()
const status = exception.getStatus()
response.status(status).json({
data:exception.message,
time:new Date().getTime(),
success:false,
path:request.url,
status
})
}
}
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Response } from './common/response';
import { HttpFilter } from './common/filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new Response());
app.useGlobalFilters(new HttpFilter());
await app.listen(3000);
}
bootstrap();
- 随便访问一个错误的路由
Nestjs 管道
在 NestJS 中,管道(Pipes)是一种用于处理传入请求数据的机制。管道主要有两个用途:转换和验证。转换指的是将输入数据转换为所需的类型,验证指的是检查输入数据是否符合预期的格式或规则
内置管道
NestJS 提供了几个内置的管道:
ValidationPipe
:用于验证输入数据ParseIntPipe
:将输入数据解析为整数ParseBoolPipe
:将输入数据解析为布尔值ParseArrayPipe
:将输入数据解析为数组ParseUUIDPipe
:将输入数据解析为 UUID- ...
使用内置管道进行转换
- 有一个路由需要接收一个 ID 参数,并且希望将其转换为整数
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
@Controller('p')
export class ItemsController {
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return `Item #${id}`;
}
}
ParseIntPipe
将把传入的id
参数从字符串转换为整数。如果传入的id
不能转换为整数,管道会抛出一个BadRequestException
- 比如请求
http://localhost:3000/p/123a
使用内置管道进行验证
- 使用
ValidationPipe
验证请求体的数据 - 首先需要定义一个 DTO(数据传输对象),并使用 class-validator 提供的装饰器来定义验证规则
//安装验证器
npm i --save class-validator
- 创建一个dto文件
在Nestjs项目中,DTO(Data Transfer Object)文件是用于数据传输的对象。DTO是一个对象,它定义了如何通过网络将数据传输到方法。简单来说,它是用来定义接口数据类型或传输数据的载体
import { IsInt, IsString, Min, MaxLength } from 'class-validator';
export class CreatePDto {
@IsString()
@MaxLength(50)
name: string;
@IsInt()
@Min(0)
quantity: number;
}
- 然后在控制器中使用
ValidationPipe
:
import { Body, Controller, Post, UsePipes, ValidationPipe } from '@nestjs/common';
import { CreatePDto } from './dto/create-p.dto';
@Controller('p')
export class PController {
@Post()
@UsePipes(new ValidationPipe())
create(@Body() createPDto: CreatePDto) {
return `Item created: ${createPDto.name} (${createPDto.quantity})`;
}
}
ValidationPipe
将验证传入的请求体是否符合CreateItemDto
中定义的规则。如果验证失败,管道会抛出一个BadRequestException
全局使用管道
- 在整个应用程序范围内使用管道,以便所有请求都经过该管道处理
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
自定义管道
自定义管道需要实现
PipeTransform
接口,并实现其transform
方法
- 在对应模块下执行
nest g pi p
生成.pipe.ts
文件
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
@Injectable()
export class PPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
return value;
}
}
- 将输入的字符串转换为大写
import { ArgumentMetadata, Injectable, PipeTransform, BadRequestException } from '@nestjs/common';
@Injectable()
export class PPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (typeof value !== 'string') {
throw new BadRequestException('Validation failed');
}
return value.toUpperCase();
}
}
import {Get, Body, Controller, Post, UsePipes, ValidationPipe, Query } from '@nestjs/common';
import { PPipe } from './p/p.pipe';
@Controller('p')
export class PController {
@Get()
@UsePipes(new PPipe())
findAll(@Query('search') search: string) {
return `Searching for items with keyword: ${search}`;
}
}
Nestjs 守卫
守卫(Guards)是一种用于实现权限控制和身份验证的机制。它们可以在请求被处理之前拦截并执行一些逻辑,以决定是否允许请求继续进行。守卫类似于中间件,但它们专注于授权逻辑
守卫是一种可以用于控制请求流的类。它实现了
CanActivate
接口,该接口定义了一个canActivate
方法。这个方法接受ExecutionContext
作为参数,并返回一个布尔值或 Promise/Observable,如果返回true
则允许请求继续,否则拒绝请求
- 可以在模块下执行
nest g gu [name]
创建
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class GGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return true;
}
}
基本使用
- 创建一个简单的身份验证守卫
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
const user = request.user;
// 示例:简单的身份验证逻辑
return !!user;
}
}
模块内全路由使用
import { Module } from '@nestjs/common';
import { GuardService } from './guard.service';
import { GuardController } from './guard.controller';
import { GGuard } from './g/g.guard';
import { APP_GUARD } from '@nestjs/core';
@Module({
controllers: [GuardController],
providers: [
GuardService,
{
provide: APP_GUARD,
useClass: GGuard,
},
],
})
export class GuardModule {}
控制器级别使用
- 在控制器上使用
@UseGuards()
装饰器
import { Controller, Get, UseGuards } from '@nestjs/common';
import { GGuard } from './g/g.guard';
@Controller('guard')
@UseGuards(GGuard)
export class CatsController {
@Get()
findAll() {
return 'This action returns all cats';
}
}
方法级别的使用
- 在控制器的方法上使用
@UseGuards()
装饰器
import { Controller, Get, UseGuards } from '@nestjs/common';
import { GGuard } from './g/g.guard';
@Controller('guard')
export class CatsController {
@Get()
@UseGuards(GGuard)
findAll() {
return 'This action returns all cats';
}
}
全局使用
app.useGlobalGuards(new RoleGuard())
自定义装饰器
- 自定义参数装饰器:用于提取和处理参数
- 自定义方法装饰器:用于在方法执行前后添加逻辑
- 自定义类装饰器:用于在类级别添加元数据或逻辑
中间件、守卫、拦截器、管道、dto、自定义参数装饰器、控制器
执行顺序
- 中间件:最先执行,用于处理请求和响应的预处理
- 守卫:中间件之后执行,用于实现权限控制和身份验证,主要作用是决定请求是否被允许继续执行
- 拦截器:守卫之后执行,用于在请求和响应的处理过程中进行额外的逻辑处理。拦截器分请求前和响应后两个阶段
- 管道:用于验证和转换请求数据,会在路由处理程序处理请求之前运行
- DTO验证:如果使用了验证管道(如:
ValidationPipe
),则管道会对传入的 DTO 数据进行验证
- DTO验证:如果使用了验证管道(如:
- 自定义参数装饰器
- 控制器方法:在所有的预处理之后,实际的控制器方法被执行,处理业务逻辑
在 NestJS 中,虽然中间件、守卫、拦截器、管道、DTO、装饰器和控制器有各自的职责和最佳实践用法,但在某些情况下,它们的功能是可以重叠和兼容的。例如,数据转换和验证可以在多个地方实现,但每个组件在架构中的位置和职责是不同的
转载自:https://juejin.cn/post/7372078061897154597