Nest.js

Nestjs中文文档链接
TypeORM 中文文档
小满视频
在这里插入图片描述

1. 安装Nest.js

  • 安装脚手架
npm i -g @nestjs/cli
  • 创建nestjs工程
nest new
  • 工程目录
    在这里插入图片描述
  • app.module.ts 根模块用于处理其他类的引用与共享。
  • app.controller.ts 常见功能是用来处理http请求(处理请求的路径),以及调用service层的处理方法
  • app.service.ts 封装通用的业务逻辑、与数据层的交互(例如数据库)、其他额外的一些三方请求

2. CURD生成器

nest g resource user   
 D:\vue\nestjs1>nest g resource user // 使用哪种传输层协议
? What transport layer do you use? REST API 
/*
REST API:选择 REST API 表示你将创建一个基于 HTTP 协议的 RESTful 服务。这种服务通常使用标准的 HTTP 方法(如 GET、POST、PUT、DELETE)来处理请求。
*/
? Would you like to generate CRUD entry points? Yes
/*
是否自动生成 CRUD(Create, Read, Update, Delete)操作
*/
  • 自动生成以下文件
    在这里插入图片描述
  • nest g resource user 这条命令不仅生成了图中的文件,同时生成了/user路径
  • 通过不同的请求会执行CURD操作
// controller文件@Post()	// post请求会新增数据create(@Body() createUserDto: CreateUserDto) {return this.userService.create(createUserDto);}@Get()	// get请求获取全部数据findAll() {return this.userService.findAll();}// 请求的地址:http://localhost:3000/user/1@Get(':id')	// 获取id为1的数据findOne(@Param('id') id: string) {return this.userService.findOne(+id);}@Patch(':id')	// 修改id为1的数据update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {return this.userService.update(+id, updateUserDto);}@Delete(':id')	// 删除id为1的数据remove(@Param('id') id: string) {return this.userService.remove(+id);}

3. 版本控制

  1. 修改main.ts
async function bootstrap() {const app = await NestFactory.create(AppModule);app.enableVersioning({type: VersioningType.URI, // 通过url传递版本信息});await app.listen(3000);
}
  • 浏览器通过什么途径传递给服务器版本信息
export declare enum VersioningType {URI = 0,	// rul中传递HEADER = 1,	// 请求头中MEDIA_TYPE = 2,		// 通过 Content-Type HTTP 头中的媒体类型CUSTOM = 3	// 自定义
}
  1. 通过url接收版本信息
  • 浏览器中的版本信息写在端口后面,并且加字母v
http://localhost:3000/v1/user
  • 所有的请求都需要版本控制
@Controller({version: '1',path: 'user'
})
  • 单独的某个请求需要版本控制
// 请求地址:http://localhost:3000/v1/user/1@Get(':id')@Version('1')findOne(@Param('id') id: string) {return this.userService.findOne(+id);}

4. nest创建文件命令

nest g [命令] [文件名]
  • 所有的命令
名称简写描述翻译
applicationapplicationGenerate a new application workspace生成新的应用程序工作区
classclGenerate a new class生成新类
configurationconfigGenerate a CLI configuration file生成CLI配置文件
controllercoGenerate a controller declaration生成控制器
decoratordGenerate a custom decorator生成自定义装饰器
filterfGenerate a filter declaration生成筛选器
gatewaygaGenerate a gateway declaration生成网关
guardguGenerate a guard declaration生成守卫
interceptoritcGenerate an interceptor declaration生成拦截器
interfaceitfGenerate an interface生成接口
librarylibGenerate a new library within a monorepo在monorepo中生成新库
middlewaremiGenerate a middleware declaration生成中间件
modulemoGenerate a module declaration生成模块
pipepiGenerate a pipe declaration生成管道
providerprGenerate a provider declaration生成提供者
resolverrGenerate a GraphQL resolver declaration生成GraphQL解析器
resourceresGenerate a new CRUD resource生成新的CRUD资源
servicesGenerate a service declaration生成服务
sub-appappGenerate a new application within a monorepo在monorepo中生成新应用程序

5. 控制器(使用者) controller.ts

控制器负责处理传入的请求和向客户端返回响应,不做具体的逻辑处理
@Controller() 修饰的类就是控制器

路由

@Controller('user')
export class UserController {constructor(private readonly userService: UserService) {}@Post("login")  // POST请求,路由:/user/logincreate() {//todoreturn {message:"登录成功"} // nest推荐使用}@Get('code')	// GET请求,路由 /user/codecreateCode(@Response response) {//todoresponse.send({message:"返回的数据"})}

响应

  1. 上面示例中当return的是基本类型 (string、number、boolean)直接发送值;如果return的是对象和数组则发送JSON序列化;再加上200的状态码
  2. 参数中有@Response装饰器时,将 Nest 置于该处理函数的特定于库(Library-specific mode)的模式下,并负责管理响应。必须通过调用 response 对象(例如,res.json(…)res.send(…))发出某种响应,否则 HTTP 服务器将挂起。

常用的装饰器

  • 方法装饰器
装饰器作用使用
@HttpCode()设置返回的状态码@HttpCode(500) //返回状态码500
@header()自定义响应头可以使用 @header() 装饰器或@Res() res.header()
@Redirect()重定向@Redirect(‘https://nestjs.com’, 301)或res.redirect()
  • 参数装饰器
装饰器相当于使用
@Request(),@Req()request
@Response(),@Res()response
@Next()
@Session()req.session
@Param(key?: string)req.params/req.params[key]get请求获取浏览器传递的数据
@Body(key?: string)req.body/req.body[key]发送post请求时接收数据
@Query(key?: string)request.query/request.query[key]浏览器发送get请求,接收数据
@Headers(name?: string)req.headers/req.headers[name]

1. @Request():请求头

无论什么类型的请求,所有的信息全部都在请求头中
// 浏览器发送请求 :/user?id=1
// 服务器代码@Get()findAll(@Request() req) {console.log(req.query);return {code:200} }
// 结果:{ id: '1' }

2. @Query(key?: string):get请求的数据

 相当于request.query
// 浏览器发送请求 :/user?id=1&name=tom@Get()findAll(@Query() query) {console.log(query); // { id: '1', name: 'tom' }return {code:200} }@Get()findAll(@Query("id") query) {console.log(query);  // 1return {code:200} }

3. @Body(key?: string):POST请求的数据

	  相当于request.body
/* 浏览器:/user
数据:Body 类型 : application/json
{"id":1,"name":"lili"
}
*/ 
//服务器代码@Post()create(@Body() body) {console.log(body) // { id: 1, name: 'lili' }return  { code:200 }}@Post()create(@Body("name") body) {console.log(body)	// lilireturn  { code:200 }}

4. @Param(key?: string):浏览器使用/1的方式传递数据

// 浏览器:/user/123// 服务器代码@Get(':id')findOne(@Param('id') id: string) {console.log(id)	// 123return { code:200 }} @Get(':id')findOne(@Request() req ,@Param('id') id: string) {console.log(req.params);	//{ id: '123' }console.log(id)	// 123return JSON.stringify({ code:200 })} 

5. @HttpCode:返回状态码

  • 只要符合这条路由返回的状态码都是500
  • 用于找不到某一条路由时重定向或者其他操作
  @Get()@HttpCode(500)findAll() {return this.userService.findAll();}

6. 提供者service.ts

  • @Injectable()修饰的类
  • 提供给控制器使用,用于逻辑处理
// 提供者 user.service.ts
import { Injectable } from '@nestjs/common';@Injectable()
export class UserService {findOne(id: number) {return `This action returns a #${id} user`;}
}
//	控制器 user.controller.ts
import { UserService } from './user.service'; // 导入提供者@Controller('user')
export class UserController {// 通过类构造函数注入提供者constructor(private readonly userService: UserService) {}	// @Get(':id')findOne(@Param('id') id: string) {// 调用提供者中的方法return this.userService.findOne(+id);}
}

7. 模块 module

给controller和service拉皮条的

  • @Module() 装饰器装饰的一个对象
@Module({controllers: [AppController],providers: [AppService],imports: [UserModule, User1Module],
})
项目Value
providers由 Nest 注入器实例化的提供者,并且可以至少在整个模块中共享
controllers必须创建的一组控制器
imports导入模块的列表,这些模块导出了此模块中所需提供者
exports由本模块提供并应在其他模块中可用的提供者的子集。

module中的提供者providers

小满视频-提供者
官网链接

1. 标准提供者

(1)在 App.service.ts@Injectable() 装饰器声明 AppService类是一个可以由Nest IoC容器管理的类。

import { Injectable } from '@nestjs/common';@Injectable()
export class AppService {getHello(): string {return 'Hello World!';}
}

(2)在 App.controller.ts 中通过构造函数声明了 AppService 注入的实例

constructor(private readonly appService: AppService) {}

(3)在 app.module.ts 中,我们将标记 AppServiceproviders

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { User1Module } from './user1/user1.module';@Module({controllers: [AppController],providers: [AppService],imports: [UserModule, User1Module],
})
export class AppModule {}

(4)providers: [AppService],的完整写法

  providers: [{provide: AppService,	// 名字可以随便起useClass: AppService,},],

(5) provide为别名

  providers: [{provide: "abc",useClass: AppService,},],
  • 控制器中使用@Inject("abc")
  constructor(@Inject("abc") private readonly appService: AppService) {}

2. 值提供者

  • module
  providers: [AppService,{provide: 'AAA',	// 提供者的名字// 提供者提供的数据,数据可以是任何类型useValue: { name: 'xiaoming', age: 18 },	},],
  • controller
import { Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';
// 类型定义
type UserInfo = {	name: string;age: number;
}@Controller()
export class AppController {constructor(private readonly appService: AppService,// 声明  UserInfo的实例 userInfo@Inject('AAA') private readonly userInfo: UserInfo) {}@Get()getHello(): string {// 通过实例访问数据return `name:${this.userInfo.name} age:${this.userInfo.age}`;}
}

3. 工厂提供者

  • module
  providers: [AppService,{provide: 'AAA',useFactory: () => {return 'AAA的工厂模式';}},],
  • controller
  constructor(private readonly appService: AppService,@Inject('AAA') private readonly abc:string) {}

4. 异步提供者

{provide: 'ASYNC_CONNECTION',useFactory: async () => {const connection = await createConnection(options);return connection;},
}

共享模块

官网链接

  1. user.module文件中导出UserService,则在所有 imports: [UserModule]的模块中都可以使用UserService
  2. 自己的话总结:"当前module"中导出service,则其他任何导入"当前module"module,都可以在controller中使用"当前module"service
// 当前module:UseModule
@Module({controllers: [UserController],providers: [UserService],exports: [UserService] // 导出 service
})
export class UserModule {}
// 其他module
@Module({imports: [UserModule], // 导入了 UserModulecontrollers: [AppController], // 导入以后,在控制器中就能使用UserModule中的serviceproviders: [AppService],})
export class AppModule {}

全局模块@Global()

  • 在根模块中引入全局模块,直接可以在子组件的controller中使用
  1. 自定义一个全局模块,路径:/src/config/config.module.ts
import { Global, Module } from "@nestjs/common";
@Global()	// 声明该模块为全局模块
@Module({providers: [{ provide: 'CONFIG_OPTIONS', useValue: { host: 'localhost', port: 77777 } },],exports: ['CONFIG_OPTIONS'],
})export class ConfigModule {}
  1. 在根模块中引入
import { ConfigModule} from './config/config.module';imports: [AAAModule, BBBModule, ConfigModule],
  1. 在其他模块的构造函数中使用
    @Inject('CONFIG_OPTIONS') private readonly options

动态模块

使用类的静态方法,创建动态模块

  1. 调用时不带参数,使用静态模块中的数据
  2. 调用时带参数,使用动态模块中的数据

在这里插入图片描述

8. 中间件

1. 基本使用方法

  • 创建中间件
nest g mi [路径/名字]
  • 创建中间件文件

next()和res.send()这两个方法只能使用一个,不可同时使用

import { Injectable, NestMiddleware } from '@nestjs/common';
import {Request,Response,NextFunction} from 'express';@Injectable()
export class UserMiddleware implements NestMiddleware {use(req: Request, res: Response, next: NextFunction) {/* 可以执行以下任务1. 执行任何代码2. 对请求和响应对象进行更改3. 调用堆栈中的下一个中间件函数4. 如果在这里没有结束请求(发送response),必须调用next()*/console.log('user middleware');next();}
}
  • 将中间件与路由绑定
import { Global, Module,NestModule,MiddlewareConsumer } from '@nestjs/common';
import { UserMiddleware} from './user.middleware';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({controllers: [UserController],providers: [UserService],exports: [UserService]
})
// 使用中间件
export class UserModule implements NestModule {// 通过绑定中间件与路由关系,实现全局中间件configure(consumer: MiddlewareConsumer): any{consumer.apply(UserMiddleware) // 绑定中间件.forRoutes('user'); // 绑定路由};
}
  • 实现的效果:只要访问use路由,这会在服务器端打印user middleware

2. 中间件绑定路由的其他方式

  1. 指定访问方式
import { RequestMethod } from '@nestjs/common';.forRoutes({path: 'user', // 绑定路由method: RequestMethod.GET // 绑定访问方式}); 
  • 其中的RequestMethod 是枚举
export declare enum RequestMethod {GET = 0,POST = 1,PUT = 2,DELETE = 3,PATCH = 4,ALL = 5,OPTIONS = 6,HEAD = 7,SEARCH = 8
}
  1. 直接绑定路由.forRoutes('user');,只能在访问/user时调用中间件,对/user/code不起作用

  2. 指定路由时使用通配符

      .forRoutes({path: 'user/*', // user路由下的所有路径method: RequestMethod.GET // 绑定访问方式}); 
  1. 指定控制器
    consumer.apply(UserMiddleware) // 绑定中间件.forRoutes(UserController); // 指定控制器};

3. 函数式中间件,并应用于全局

  1. 创建函数式中间件
// ./app.middleware.function
import {Request,Response,NextFunction} from 'express';export const  MiddleWareAll = (req:Request,res:Response,next:NextFunction)=>{console.log("全局路由")// 这里的主要使用场景是路由的白名单或黑名单next()
}   
  1. 在main.ts文件中引入并使用
import {MiddleWareAll} from './app.middleware.function';
app.use(MiddleWareAll)

4. 通过中间件插件允许跨域请求

  • 安装cors插件
npm i cors -S    
npm i @types/cors -D
  • 在main.ts文件中配置
import * as cors from 'cors';
...app.use(cors()) // 这条代码的位置必须在所有的中间件之前

9. 拦截器

官网

1. 创建拦截器

import { NestInterceptor,ExecutionContext,CallHandler } from '@nestjs/common';
import {  Observable, tap } from 'rxjs';export class LoggingInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler):Observable<any>{const req = context.switchToHttp().getRequest() // 获取请求对象const res = context.switchToHttp().getResponse()    // 获取响应对象console.log('Before...');const now = Date.now();// 上面的代码执行完毕// 再执行controller中的代码//最后执行到下面的代码return next.handle().pipe(tap(()=>console.log(`After... ${Date.now() - now}ms`)))}
}

2. 拦截器的使用

  • 在controller的类上使用
// 导入拦截器
import { UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from '../Interceptors/Interceptor1';@Controller('upload')
@UseInterceptors(LoggingInterceptor)
export class UploadController
  • 在controller的方法上使用
  @Get()@UseInterceptors(LoggingInterceptor)getFile(): string {...}
  • 全局使用,main.ts
import {LoggingInterceptor} from './Interceptors/Interceptor1';async function bootstrap() {const app = await NestFactory.create(AppModule);// 直接使用拦截器的实例,没有使用依赖注入app.useGlobalInterceptors(new LoggingInterceptor());await app.listen(3000);
}
bootstrap();
  • module中设置拦截器,可全局或者模块中使用
import { LoggingInterceptor } from './Interceptors/Interceptor1';
@Module({controllers: [AppController],providers: [AppService,{provide:"APP_INTERCEPTOR",useClass:LoggingInterceptor}],
})
export class AppModule {}

3. 响应映射

  • 使用map等运算符

使用运算符,则在controller中不能使用res返回信息

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';export interface Response<T> {data: T;
}@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {return next.handle().pipe(map(data => ({ data })));}
}

4. 异常拦截器

import { Catch,HttpException,ExceptionFilter,ArgumentsHost } from "@nestjs/common";
import { Request,Response } from "express";@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost){const req:Request = host.switchToHttp().getRequest();const res: Response = host.switchToHttp().getResponse();// 获取异常时的状态码const status: number = exception.getStatus();// 链式调用status和json,status:设置状态码,json的参数是返回的数据res.status(status).json({	// 此处不设置状态码,返回的状态码是 200timestamp: new Date().toISOString(),path: req.url,error: exception.getResponse()})}
}
  • 全局使用 main.ts
import { HttpExceptionFilter } from './Interceptors/filter';
async function bootstrap() {const app = await NestFactory.create<NestExpressApplication>(AppModule);app.useGlobalFilters(new HttpExceptionFilter())await app.listen(3000);
}
bootstrap();

10. 管道

管道有两个典型的应用场景:
- 转换:管道将输入数据转换为所需的数据输出(例如,将字符串转换为整数)
- 验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常

1. 简单数据类型的内置管道

  • ParseBoolPipe 布尔类型
  • ParseFloatPipe 浮点数
  • ParseEnumPipe 枚举
  • ParseArrayPipe 数组
  • ParseUUIDPipe uuid

当使用上述类型时,会先转换数据的类型,如果转换不成功则抛出错误,

import { ParseIntPipe } from '@nestjs/common';@Get(':id')findOne(@Param('id',ParseIntPipe) id: string) { // 虽然已经在此定义了id的类型为string,使用管道后,转换为numberconsole.log(typeof(id)); // 输出:numberreturn this.pipeService.findOne(+id);}

2. 自定义管道

① 在需要自定义管道的目录中创建文件

nest g pipe [文件名]

② 自定义管道的条件

import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';@Injectable()
export class CustomPipe implements PipeTransform {transform(value: any, metadata: ArgumentMetadata) {// 自定义条件来约束数据// 这里的示例并不符合管道的职责,管道是验证数据的if (value.age >= 18) {value.age = 17}return value;}
}

③在controller中绑定

import {CustomPipe} from './custom/custom.pipe'
@Controller('pipe')
export class PipeController {constructor(private readonly pipeService: PipeService) {}@Post()create(@Body(CustomPipe) createPipeDto: CreatePipeDto) {return createPipeDto// return this.pipeService.create(createPipeDto);}

在这里插入图片描述

3. 使用Joi做数据验证

  1. 安装joi
npm install --save joi
npm install --save-dev @types/joi
  1. 创建joi的验证文件
import { ArgumentMetadata, Injectable, PipeTransform,BadRequestException  } from '@nestjs/common';
import { ObjectSchema } from 'joi';
import * as Joi from 'joi';
@Injectable()
export class JoiValidationPipe implements PipeTransform {constructor(private schema: ObjectSchema) {}transform(value: any, metadata: ArgumentMetadata) {const { error } = this.schema.validate(value);if (error) {throw new BadRequestException('Validation failed');}return value;}
}
// 验证规则
export const createPipeSchema = Joi.object({// 假设 CreatePipeDto 包含 name 和 age 属性name: Joi.string().min(3).max(30).required(),age: Joi.number().integer().min(18).max(100).required(),});
  1. 绑定验证
import { JoiValidationPipe,createPipeSchema  } from './joi-validation/joi-validation.pipe'@Post()@UsePipes(new JoiValidationPipe(createPipeSchema))create(@Body() createPipeDto: CreatePipeDto) {return createPipeDto// return this.pipeService.create(createPipeDto);}

在这里插入图片描述

在这里插入图片描述

4. 类验证器

验证装饰器中文文档

  • 安装
npm i --save class-validator class-transformer
  • 生成CRUD资源模块时,同时生成两个数据类型文件.dto.ts
  • 插件class-validator提供了很多验证规则,使用装饰器就可对数据进行验证
// creat-pipe.dto.ts
import { IsString,IsNumber,IsNotEmpty,Length } from "class-validator";export class CreatePipeDto {@IsString({message:'name必须是字符串'})@IsNotEmpty({message:'name不能为空'})@Length(3, 10,{message:'name长度必须在3到10之间'})name: string;@IsNumber()@IsNotEmpty()age: number;
}
  • 管道
// ./custom/custom.pipe 验证管道
import { ArgumentMetadata, Injectable, PipeTransform,BadRequestException  } from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
@Injectable()
export class CustomPipe implements PipeTransform {async transform(value: any, metadata: ArgumentMetadata) {// 在此可以修改value的数据,例如将null修改为""// 将dto文件中的数据类型 和 接收到的数据 合并成一个实例// value是前端发送的req.body,通过controller转发过来的const DTO = plainToInstance(metadata.metatype,value)// 验证DTO实例,errors 是DTO验证后的所有错误信息const errors = await validate(DTO)console.log(errors)// 错误信息的条数大于1,说明数据有验证未通过if (errors.length > 0) {throw new BadRequestException(errors)}return value;}
}
  • 在绑定管道
import { CreatePipeDto } from './dto/create-pipe.dto';
import {CustomPipe} from './custom/custom.pipe'@Post()create(@Body(CustomPipe) createPipeDto: CreatePipeDto) {return createPipeDto// return this.pipeService.create(createPipeDto);}
  • 管道中返回的结果
POST /pipe	{"name":"a","age":""}{"statusCode": 400,"timestamp": "2024-09-13T14:43:14.775Z","path": "/pipe","message": "Bad Request Exception","error": {"message": [{"target": {"name": "a","age": ""},"value": "a","property": "name","children": [],"constraints": {"isLength": "name长度必须在3到10之间"}},{"target": {"name": "a","age": ""},"value": "","property": "age","children": [],"constraints": {"isNotEmpty": "age should not be empty","isNumber": "age must be a number conforming to the specified constraints"}}],"error": "Bad Request","statusCode": 400}
}
  • ./custom/custom.pipe管道代码中的metadata,有三个属性,看图
    在这里插入图片描述

5. 类验证器,全局设置

  • 这种方式使用管道,只能验证数据类型,无法改变数据,例如把null改为""
  • 全局使用Pipe,无需与controller绑定,也能返回错误信息
// main.ts
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {const app = await NestFactory.create<NestExpressApplication>(AppModule);app.useGlobalPipes(new ValidationPipe())await app.listen(3000);
}
bootstrap();

在这里插入图片描述

11. 守卫

官网中文文档

  • 作用:鉴权

1. 基本使用

  1. 创建守卫
    • 在需要守卫的模块中创建守卫文件
nest g gu [文件名]
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
import type {Request} from 'express';
@Injectable()
export class RoleGuard implements CanActivate {constructor(private reflector: Reflector){}canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {// 获取controller中key=roles的SetMetadata装饰器中的参数// 第一个参数是SetMetadata的key,第二个是SetMetadata修饰的函数体const roles = this.reflector.get<string[]>('users', context.getHandler());const req:Request = context.switchToHttp().getRequest();// 获取通过query传入的权限const user:string = req.query.user as string;// 如果在roles中包含user则返回true否则返回falseif(roles.includes(user)){return true} else {return false;}}
}
  1. 在controller的类使用UseGuards装饰器
import { UseGuards } from '@nestjs/common';@Controller('guard')
@UseGuards(RoleGuard)
  1. 在controller类中的方法设置权限
    • SetMetadata有两个参数:第一个参数是该条规则的名字,第二个就是可访问的角色,可以是多个角色
  @Get()@SetMetadata("users","admin","user")

2. 在全局中使用守卫

app.useGlobalGuards(new GlobaRulesGuard)

3. 自定义装饰器

nest g d [文件前缀名]
import { SetMetadata } from '@nestjs/common';
export const UserRoles = (...args: string[]) => SetMetadata('users', args);
  • 在controller中使用
import { UserRoles } from './decorators/decorators.decorator';@Get()@UserRoles ("admin","guest")

常用的插件

1. express-session

  • 安装express-session和类型声明
npm i express-session --save
npm i @types/express-session -D
  • 在main.ts文件中引入
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as session from 'express-session' // async function bootstrap() {const app = await NestFactory.create(AppModule);app.use(session({secret:"xm"})) // await app.listen(3000);
}
bootstrap();
  • 配置项
项目用途默认值示例
secret用于签名会话 ID 的 cookie。string类型必选项
resave是否在每次请求时重新保存会话,即使未修改true
saveUninitialized是否为尚未初始化的会话创建新的会话对象。true
cookie设置 cookie 的选项。{ maxAge: 1000 * 60 * 60 * 24 * 30 }
name设置会话 ID 的 cookie 名称“connect.sid”“session_id”
store存储会话数据的对象。使用 connect-mongo 连接 MongoDB
proxy是否信任代理服务器false
rolling是否在每次请求时刷新会话的过期时间false
  • cookie的子选项
项目作用
maxAge设置会话 cookie 的最大生存时间(毫秒)。例如,1000 * 60 * 60 * 24 * 30 表示 30 天。
httpOnly设置 cookie 的 httpOnly 标志,防止 JavaScript 访问,增加安全性。
secure设置 cookie 的 secure 标志,在生产环境中应设置为 true,确保 cookie 只能通过 HTTPS 传输。
sameSite设置 cookie 的 SameSite 属性,增加安全性。默认值“lax“;”strict“ 表示只能在相同站点下发送 cookie。
  • 示例
  app.use(session({secret: "xm", // 加盐。必选配置项。用于签名会话 ID 的 cookie,确保会话数据的安全性resave: false, // 是否在每次请求时重新保存会话,即使未修改(推荐设置为 false)saveUninitialized: false, // 是否为尚未初始化的会话创建新的会话对象(推荐设置为 false)cookie: {maxAge: 1000 * 60 * 60 * 24 * 30, // 设置会话 cookie 的最大生存时间(30 天)httpOnly: true, // 设置 cookie 的 httpOnly 标志,防止 JavaScript 访问(增加安全性)secure: false, // 设置 cookie 的 secure 标志,在生产环境中应设置为 true(仅通过 HTTPS 传输)sameSite: 'strict' // 设置 cookie 的 SameSite 属性,增加安全性(strict 表示只能在相同站点下发送 cookie)},name: 'session_id', // 设置会话 ID 的 cookie 名称,默认为 "connect.sid"rolling: true, // 是否在每次请求时刷新会话的过期时间(滚动更新)proxy: false // 是否信任代理服务器(在使用反向代理时设置为 true)}));

2. 验证码svgCaptcha

svgCaptcha:svg图片

  • 安装
npm i svg-captcha -S
  • 使用
import  * as svgCaptcha from 'svg-captcha';const captcha =  svgCaptcha.create({ size: 4,  // 图片中有几个字符fontSize: 50, // 字体大小width: 100, // 图片宽度height: 40, // 图片高度background: '#cc9966' // 背景颜色})
console.log(captcha)
  • 结果
{"text": "3X1i", // 图片中的字母// data中就是svg图形"data": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"40\" viewBox=\"0,0,100,40\"><rect width=\"100%\" height=\"100%\" fill=\"#cc9966\"/><path d=\"M15 9 C68 38,51 13,91 39\" stroke=\"#4fd3d3\" fill=\"none\"/><path fill=\"#595959\" d=\"M14.06 ........12Z\"/></svg>"
}

技术

1. 上传文件

官网链接

为了处理文件上传,Nest 提供了一个内置的基于 multer 中间件包的 Express 模块。Multer 处理以 multipart/form-data 格式发送的数据

1. 基本使用

  1. 安装类型声明文件
npm i -D @types/multer
  1. 在控制器中使用
import { Controller, Get, Post, Body, Patch, Param, Delete, UseInterceptors, UploadedFile } from '@nestjs/common';
import { UploadService } from './upload.service';
import { FileInterceptor } from '@nestjs/platform-express';
import { Express } from 'express';
@Controller('upload')
export class UploadController {constructor(private readonly uploadService: UploadService) {}@Post('img')@UseInterceptors(FileInterceptor('file'))create(@UploadedFile() file: Express.Multer.File ) {console.log(file)}
}
  1. 结果
// 上传路径:/upload/img
{fieldname: 'file',originalname: '0bb9ad608ead12b4e8ead0d172fa6300.jpg',encoding: '7bit',mimetype: 'image/jpeg',buffer: <Buffer ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 00 01 00 00 ff db 00 43 00 02 02 02 02 02 01 02 02 02 02 03 02 02 03 03 06 04 03 03 03 03 07 05 05 04 ... 68607 more bytes>,size: 68657
}

2. 将上传文件保存到磁盘中

  • 在module文件中,设置将上传的文件写入到磁盘中
import { Module } from '@nestjs/common';
import { UploadService } from './upload.service';
import { UploadController } from './upload.controller';
import { MulterModule} from '@nestjs/platform-express';
import { extname, join} from 'path';
import { diskStorage } from 'multer';@Module({imports:[MulterModule.register({// 自定义存储storage:diskStorage({ // diskStorage:保存到磁盘上// 设置保存文件的路径destination: join(__dirname,'../images'),// 设置文件的文件名filename:(req,file,callback)=>{// extname(file.originalname):获取文件的后缀名const filename = Date.now()+extname(file.originalname);return callback(null,filename);}})})],controllers: [UploadController],providers: [UploadService],
})
export class UploadModule {}
  • 结果:文件上传后路径 dist/images

3. 访问服务器中的静态资源

  • 修改main.ts文件
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';async function bootstrap() {const app = await NestFactory.create<NestExpressApplication>(AppModule);// 配置静态资源的路径app.useStaticAssets(join(__dirname,'images'),{// 设置虚拟路径,浏览器访问上面的静态资源时需要加 /img 路径prefix: '/img',})await app.listen(3000);
}
bootstrap();
  • 结果:浏览器访问:http://localhost:3000/img/1726123176794.jpg时返回一张图片

2. 下载文件

1. 直接下载

import { Response } from 'express';
import { join } from 'path';
@Controller('upload')
export class UploadController {constructor(private readonly uploadService: UploadService) {}@Get('export')// 注意此处的Response是从express引入的downLoad(@Res() res:Response) {const url = join(__dirname,'../images/1726123176794.jpg')res.download(url)}
}
  • 结果:浏览器:http://localhost:3000/upload/export下载图片文件

2. 通过流处理

官网

import { UploadService } from './upload.service';
import { Controller, Get, StreamableFile, Response } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from 'path';
@Controller('upload')
export class UploadController {constructor(private readonly uploadService: UploadService) {}@Get()getFile(@Response({ passthrough: true }) res): StreamableFile {const file = createReadStream(join(__dirname, '../images/217.jpeg'));console.log(file)res.set({'Content-Type': 'image/jpeg',// inline:直接在浏览器中打开文件;attachment:下载文件'Content-Disposition': 'inline; filename="217.jpeg"',});return new StreamableFile(file);}
}

3. 压缩

小满视频中是压缩为zip包后再发送流文件

3. 数据库

官网链接

1. 连接数据库和实体类

  1. 安装nestjs与数据库的关系映射器
npm install --save @nestjs/typeorm typeorm mysql2
  1. 在app.moudel.ts中配置数据库
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
// 引入orm
import { TypeOrmModule } from '@nestjs/typeorm';
// 引入实体类
import { User } from './user/entities/user.entity';@Module({controllers: [AppController],providers: [AppService],imports: [UserModule,// 配置数据库TypeOrmModule.forRoot({type: "mysql", //数据库类型username: "root", //账号password: "******", //密码host: "localhost", //hostport: 3306, //database: "demo", //库名// 载入实体类// entities: [__dirname + '/**/*.entity{.ts,.js}'], // 通过路径指定实体类// entities: [User], // 指定已经引入的实体类autoLoadEntities:true, //在所有模块的module的forFeature()方法中注册实体都将自动添加到entities的数组中// 生产环境中建议关闭synchronizesynchronize:true, //是否自动将实体类同步到数据库retryDelay:500, //重试连接数据库间隔retryAttempts:10,//重试连接数据库的次数}),],
})
export class AppModule {}
  1. user模块中的实体类
// ./entities/user.entity
import { Entity, Column, PrimaryGeneratedColumn,CreateDateColumn } from 'typeorm';
// 实体类的修饰符
@Entity()
export class User {// 唯一且自增@PrimaryGeneratedColumn()id: number;// @Column:定义列@Column()name: string;@Column()desc: string;@CreateDateColumn({type:"timestamp"})create_Time: Date;
}
  1. 在user模块的module中绑定实体类
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
@Module({controllers: [UserController],providers: [UserService],exports: [UserService],imports: [TypeOrmModule.forFeature([User])]
})
  1. 至此将会在数据库中自动创建user这张表

2. 定义实体类中各字段

小满

3. 在server中操作数据表

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Like, Repository } from 'typeorm';
@Injectable()
export class UserService {// 实体类实例化constructor(@InjectRepository(User) private readonly userRepository: Repository<User>,) {}create(createUserDto: CreateUserDto) {// 给实体类的属性赋值,通过sava方法保存到数据库中const data = new User();data.name = createUserDto.name;data.desc = createUserDto.desc;return this.userRepository.save(data);}// query传入的参数:关键字(当关键字为"",返回所有数据);第几页;每页几条数据async findAll(query: { keyWord: string; currentPage: number; pageSize: number }) {// find方法:按条件查询const data =   await this.userRepository.find({where: { name: Like(`%${query.keyWord}%`) }, // where条件order: { id: "ASC" }, // 按照id 升序排列skip: (query.currentPage - 1) * query.pageSize, // 从第几条数据开始take: query.pageSize, // 取几条数据});// 按条件查询,符合查询条件的数据总条数const total = await this.userRepository.count({where: { name: Like(`%${query.keyWord}%`) },})return {data,total}}update(id: number, updateUserDto: UpdateUserDto) {// update方法,更新数据return this.userRepository.update(id, updateUserDto);}remove(id: number) {// delete方法,删除数据return this.userRepository.delete(id);}
}

4. 表的一对多关系

  • User表和Tag表,一个User有多个Tag的一对多关系

user.entity.ts文件

import { Entity, Column, PrimaryGeneratedColumn,CreateDateColumn, OneToMany } from 'typeorm';
import { Tags } from './tags.entity';
// 实体类的修饰符
@Entity()
export class User {// 唯一且自增@PrimaryGeneratedColumn()id: number;// 列@Column()name: string;@Column()desc: string;@CreateDateColumn({type:"timestamp"})create_Time: Date;// 一对多,主要体现在 装饰器 和 []// 第一个参数指向关联的实体类Tags// 第二个参数中的t是第一个参数指向的实体类,user是Tags实体类中列的名字@OneToMany(()=>Tags,t=>t.user)// 一对多,这里需要使用 []tags:Tags[]
}

tags.entity.ts文件

import { Entity, Column, PrimaryGeneratedColumn,CreateDateColumn, ManyToOne } from 'typeorm';
import { User } from './user.entity'
// 实体类的修饰符
@Entity()
export class Tags {// 唯一且自增@PrimaryGeneratedColumn()id: number;// 列@Column()name:string@ManyToOne(()=>User,u=>u.tags)user:User
}

前端传来的数据

// 给id为2的user添加多个标签
{"id": 2,"tags": ["red","green","blue"]
}
// createTagsDto:前端传来的数据async createTags(createTagsDto: CreateTagsDto) {// 提取中其中的tagsconst data:string[] = createTagsDto.tags// 根据传来的id找到userconst user =   await this.userRepository.findOne({where:{id:createTagsDto.id}})// user表中的tags字段const tagsList:Tags[] = []// 遍历tags,保存到tag表中for (const item of data){const tags = new Tags()tags.name = item// tag表的user字段使用上面查询出来的user对象,不是只传user的idtags.user = user// 保存到tag表中。await this.tagsRepository.save(tags)tagsList.push(tags)}user.tags = tagsList// 这里 sava 方法不会创建新的user// 在sava时如果发现存在该id则会更新user表中的tags字段await this.userRepository.save(user)return true}

5. 事务

export class TransferMoney{fromId: number;toId: number;money: number;
}
  async transfer(transferMoney: TransferMoney) {try {// 在数据库中找到要转账的两个账户const accountFrom = await this.moneyRepository.findOneBy({id: transferMoney.fromId,});const accountTo = await this.moneyRepository.findOneBy({id: transferMoney.toId,});if (!accountFrom || !accountTo) {return { message: '账户不存在' };}if (accountFrom.money < transferMoney.money) {return { message: '余额不足' };}// 开启事务.manager.transaction(async (manager) => {})固定语法await this.moneyRepository.manager.transaction(async (manager) => {// 更新转出账户的余额await manager.update(Account,{ id: transferMoney.fromId },{ money: accountFrom.money - transferMoney.money },);// 更新转入账户的余额await manager.update(Account,{ id: transferMoney.toId },{ money: accountTo.money + transferMoney.money },);});return { message: '转账成功' };} catch (e) {return { message: e };}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/54246.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何升级用 Helm 安装的极狐GitLab Runner?

本分分享如何对 Helm 安装的 Runner 进行升级。整个过程分为三步&#xff1a;1、确定 Runner 最新版本或者想要升级的版本是否存在&#xff1b;2、用 Helm upgrade 命令进行升级&#xff1b;3、升级确认。 极狐GitLab 为 GitLab 的中国发行版&#xff0c;中文版本对中国用户更…

react18基础教程系列-- 框架基础理论知识mvc/jsx/createRoot

react的设计模式 React 是 mvc 体系&#xff0c;vue 是 mvvm 体系 mvc: model(数据)-view(视图)-controller(控制器) 我们需要按照专业的语法去构建 app 页面&#xff0c;react 使用的是 jsx 语法构建数据层&#xff0c;需要动态处理的的数据都要数据层支持控制层: 当我们需要…

1730. 购买贺年卡

代码 #include<bits/stdc.h> using namespace std; struct c {int a,b; }t[1005]; int cmp(c a,c b) {return a.a>b.a; } int main() {int n,m,sum0;cin>>n>>m;for(int i1;i<m;i){cin>>t[i].a>>t[i].b;}sort(t1,t1m,cmp);for(int im;i&g…

如何在Linux下升级R版本和RStudio

一、升级R版本 在Linux上&#xff0c;R的安装通常通过包管理器完成。不同的Linux发行版&#xff08;如Ubuntu、Debian、Fedora等&#xff09;可能略有不同。下面以Ubuntu为例&#xff0c;介绍如何升级R版本。如果你使用其他发行版&#xff0c;步骤可能类似。 二.更新步骤 2.…

【可视化大屏系列】数据列表自动滚动效果

要实现列表的自动滚动效果&#xff0c;这里提供两种解决方案&#xff1a; 1.vue插件 官方文档&#xff1a;链接: vue-seamless-scroll &#xff08;1&#xff09;安装依赖 npm install vue-seamless-scroll --save&#xff08;2&#xff09;全局注册&#xff08;main.js中&a…

【机器学习】--- 自监督学习

1. 引言 机器学习近年来的发展迅猛&#xff0c;许多领域都在不断产生新的突破。在监督学习和无监督学习之外&#xff0c;自监督学习&#xff08;Self-Supervised Learning, SSL&#xff09;作为一种新兴的学习范式&#xff0c;逐渐成为机器学习研究的热门话题之一。自监督学习…

【linux-Day3】linux的基本指令<中>

【linux-Day3】linux的基本指令<中> linux下的基本指令&#x1f4e2;man&#xff1a;访问linux手册页&#x1f4e2;echo&#xff1a;把字符串写入指定文件中&#x1f4e2;cat&#xff1a;查看目标文件的内容&#x1f4e2;cp&#xff1a;复制文件或目录&#x1f4e2;mv&am…

AJAX 入门 day3

目录 1.XMLHttpRequest 1.1 XMLHttpRequest认识 1.2 用ajax发送请求 1.3 案例 1.4 XMLHttpRequest - 查询参数 1.5 XMLHttpRequest - 数据提交 2.Promise 2.1 Promise认识 2.2 Promise - 三种状态 2.3 案例 3.封装简易版 axios 3.1 封装_简易axios_获取省份列表 3…

Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约

Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约 文章目录 Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约前言版本适配一、启动FIsco Bcos区块链网络二、获取控制台文件三、配置控制台3.1 执行download_console.sh脚本3.2 拷贝控制台配置文件3.3 修…

Mac下nvm无法安装node问题

背景 最近换用mac开发&#xff0c;然后使用nvm&#xff08;版本0.40.1&#xff09;进行node安装的时候出现了一些问题 使用 nvm ls-remote发现只有 iojs 版本 原因可能是nodejs升级了某个协议导致的 解决方案 可以使用 NVM_NODEJS_ORG_MIRRORhttp://nodejs.org/dist nvm ls-re…

数据结构(八)——Java实现七大排序

一、插入排序 1.直接插入排序 public static void insertSort(int []arr){for (int i 0; i < arr.length; i) {int j i-1;int tmp arr[i];for (; j >0 ; j--) {if(arr[j] > tmp){arr[j1] arr[j];}else{break;}}arr[j1] tmp;}}直接插入排序特性总结 1. 元素集合越…

TikTok商家如何通过真人测评提高流量和销量?

在当今的社交媒体营销领域&#xff0c;TikTok&#xff08;抖音国际版&#xff09;以其独特的短视频内容和庞大的用户群体&#xff0c;成为了品牌营销和产品推广的热门平台。其中&#xff0c;真人测评作为一种有效的营销策略&#xff0c;正逐渐受到商家的青睐。本文将探讨TikTok…

华硕产品资料的查询方法

华硕网站有些奇怪&#xff0c;比如我要查询x99-pro这款已经停售的主板的资料&#xff0c;在它的首页搜索&#xff0c;搜索结果为空&#xff1a; 然后在服务支持和下载中心&#xff0c;同样搜不到。 经高人指点&#xff0c;用下面的方法&#xff0c;可以搜到&#xff1a; https…

微信小程序页面制作——婚礼邀请函(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

web基础—dvwa靶场(四)​File Inclusion

File Inclusion(文件包含) 有些 web 应用程序允许用户指定直接文件流的输入&#xff0c;或允许用户将文件上载到服务器。稍后 web 应用程序访问 web 应用程序上下文中用户提供的输入。通过这样种操作&#xff0c;web 应用程序允许恶意文件执行。 如果选择要包含的文件是目标计…

【Hot100】LeetCode—51. N 皇后

目录 1- 思路题目识别回溯 2- 实现⭐51. N 皇后——题解思路 3- ACM 实现 原题链接&#xff1a;51. N 皇后 1- 思路 题目识别 识别1 &#xff1a;给定一个整数 n &#xff0c;求解如何放置棋子的问题。 回溯 回溯三部曲 1- 回溯参数和返回值 传参 cheeseBoard、n、row 传递…

如何设置 Django 错误邮件通知 ?

Django 是一个强大的 web 框架&#xff0c;非常适合那些想要完美快速完成任务的人。它有许多内置的工具和特性&#xff0c;一个有用的特性是 Django 可以在出现错误时发送电子邮件提醒。这对开发人员和管理员非常有用&#xff0c;因为如果出现问题&#xff0c;他们会立即得到通…

改编pikachu的打靶经历(题目不全)

前言 题目很少&#xff0c;只做了一些。正常版本的&#xff0c;完整的pikachu可参考下面这个师傅写的 https://www.cnblogs.com/henry666/p/16947270.html xss &#xff08;get&#xff09;反射xss 先尝试 1 这里有长度限制&#xff0c;而且&#xff0c;我改了长度&#xf…

MySQL_数据类型简介

课 程 推 荐我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448;入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448;虚 拟 环 境 搭 建 &#xff1a;&#x1…

文生视频算法

文生视频 Sora解决问题&#xff1a;解决思路&#xff1a; CogVideoX解决问题&#xff1a;解决思路&#xff1a; Stable Video Diffusion&#xff08;SVD&#xff09;解决问题&#xff1a;解决思路&#xff1a; 主流AI视频技术框架&#xff1a; Sora Sora: A Review on Backg…