1、前置知识
1.1 装饰器
装饰器的类型
ts
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => void;
1.2 实现一个get请求装饰器
ts
import axios from "axios";
const getDecorator = (url: string) => {
return function (
target: Object,
propertyKey: string | symbol,
descriptor: any
) {
axios
.get(url)
.then((res) => {
descriptor.value(res, { state: "1" });
})
.catch((err) => {
descriptor.value(err, { state: "0" });
});
};
};
class AxiosDecorator {
name: string;
constructor() {
this.name = "你好 啊";
}
@getDecorator("https://jsonplaceholder.typicode.com/posts")
axiosGet(res: any, state: any) {
console.log(res, state);
}
}
2、nest cli 创建项目
shell
npm i -g @nestjs/cli
nest new [项目名称]
js
//创建完成后,目录结果如下
src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts
app.controller.ts | 带有单个路由的基本控制器示例。 |
---|---|
app.controller.spec.ts | 对于基本控制器的单元测试样例 |
app.module.ts | 应用程序的根模块。 |
app.service.ts | 带有单个方法的基本服务 |
main.ts | 应用程序入口文件。它使用 NestFactory 用来创建 Nest 应用实例。 |
json
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
一般的话用 npm run start:dev
3、nest cli 常用命令
nest g resource xxx 可以直接生成一套模板
4、nest 控制器
@Request(),@Req() |
req |
@Response(),@Res() |
res |
@Next() |
next |
@Session() |
req.session |
@Param(key?: string) |
req.params /req.params[key] |
@Body(key?: string) |
req.body /req.body[key] |
@Query(key?: string) |
req.query /req.query[key] |
@Headers(name?: string) |
req.headers /req.headers[name] |
@Ip() |
req.ip |
@HostParam() |
req.hosts |
ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Version,
Request,
Query,
} from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { version } from 'process';
// import { Request } from 'express';
// 给这个模块全局增加版本号
// @Controller({
// path: 'user',
// version: '1',
// })
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
// 查询参数是 @Request 装饰器的 query
// 如果放在body 里面了 是 @Request 装饰的 body
@Get()
findAll(@Request() req) {
// 也可以直接使用Query 装饰器,就不用 .query了
// console.log(req);
console.log(req.query);
return {
state: '1',
data: req.query,
};
}
@Post()
create(@Request() req) {
// 也有 @Body 装饰器
// body 如果有多个参数,只想读取某一个东西,还可以直接写 @Body('xxx') ,query 同理
console.log('post请求', req.body);
return {
state: '1',
data: req.body,
};
}
@Get(':id')
selectByID(@Request() req) {
// 也有 @Param 装饰器
return {
state: '1',
data: req.params,
};
}
@Get('select')
select() {
return {
state: '1',
data: 'select',
};
}
// @Post()
// create(@Body() createUserDto: CreateUserDto) {
// return this.userService.create(createUserDto);
// }
// // 也可以单独添加版本
// @Get()
// @Version('1')
// findAll() {
// return this.userService.findAll();
// }
// @Get(':id')
// findOne(@Param('id') id: string) {
// return this.userService.findOne(+id);
// }
// @Patch(':id')
// update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
// return this.userService.update(+id, updateUserDto);
// }
// @Delete(':id')
// remove(@Param('id') id: string) {
// return this.userService.remove(+id);
// }
}
5、providers
5.1 基本用法
控制器里面的方法就能用这个 userservice 里面的方法了
5.2 service 第二种用法,自定义名称
- 修改
user.module.ts
- 修改
user.controller.ts
js
// user.module.ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
// providers: [UserService], // 这个是简写
providers: [
{
provide: 'ddgService',
useClass: UserService,
},
],
})
export class UserModule {}
ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Version,
Request,
Query,
Inject,
} from '@nestjs/common';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
// constructor(private readonly userService: UserService) {}
// 如果自定义了 服务的名称,这里需要把名称传递进来
constructor(
@Inject('ddgService') private readonly userService: UserService,
) {}
}
5.3 自定义注入值
可以通过 providers 数组的 useValue 自定义注入值
ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
// providers: [UserService], // 这个是简写
providers: [
{
provide: 'ddgService',
useClass: UserService,
},
{
provide: 'ddgService2',
useValue: ['呆呆狗1号', '呆呆狗2号', '呆呆狗3号'],
},
],
})
export class UserModule {}
ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Version,
Request,
Query,
Inject,
} from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { version } from 'process';
@Controller('user')
export class UserController {
constructor(
@Inject('ddgService') private readonly userService: UserService,
@Inject('ddgService2') private ddgName: string[],
) {}
@Get()
findAll(@Request() req) {
// 也可以直接使用Query 装饰器,就不用 .query了
// console.log(req);
console.log(req.query);
console.log('providers 的 ddgService2的值', this.ddgName);
return {
state: '1',
data: req.query,
};
}
}
5.4 工厂模式
ts
// user.service2
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UserService2 {
getSay() {
return '你好啊,我是工厂模式下注入的';
}
}
ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserService2 } from './user.service2';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
// providers: [UserService], // 这个是简写
providers: [
{
provide: 'ddgService',
useClass: UserService,
},
{
provide: 'ddgService2',
useValue: ['呆呆狗1号', '呆呆狗2号', '呆呆狗3号'],
},
UserService2,
{
provide: 'ddgService3',
inject: [UserService2],
useFactory: (UserService2: UserService2) => {
console.log(UserService2.getSay());
// 这里也可以使用 async await ,异步模式也可以
return 123;
},
},
],
})
export class UserModule {}
ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Version,
Request,
Query,
Inject,
} from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { version } from 'process';
@Controller('user')
export class UserController {
constructor(
@Inject('ddgService') private readonly userService: UserService,
@Inject('ddgService2') private ddgName: string[],
@Inject('ddgService3') private getSay: number,
) {}
@Get()
findAll(@Request() req) {
// 也可以直接使用Query 装饰器,就不用 .query了
// console.log(req);
console.log(req.query);
console.log('providers 的 ddgService2的值', this.ddgName);
console.log(this.getSay);
return {
state: '1',
data: req.query,
};
}
}
6、模块
每个 Nest 应用程序至少有一个模块,即
根模块
。根模块是 Nest 开始安排应用程序树的地方。事实上,根模块可能是应用程序中唯一的模块,特别是当应用程序很小时,但是对于大型程序来说这是没有意义的。在大多数情况下,您将拥有多个模块,每个模块都有一组紧密相关的功能
6.1 共享模块
例如 想让 app 模块 访问
user模块的service
1、定义一个函数
ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UserService2 {
getSay() {
return '你好啊,我是工厂模式下注入的';
}
getLook() { // 一会要调用的函数
return '我是user 模块的 service';
}
}
2、在 user模块里面 导出 这个服务
ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserService2 } from './user.service2';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
// providers: [UserService], // 这个是简写
providers: [
{
provide: 'ddgService',
useClass: UserService,
},
{
provide: 'ddgService2',
useValue: ['呆呆狗1号', '呆呆狗2号', '呆呆狗3号'],
},
UserService2,
{
provide: 'ddgService3',
inject: [UserService2],
useFactory: (UserService2: UserService2) => {
console.log(UserService2.getSay());
return 123;
},
},
],
exports: [UserService2],
})
export class UserModule {}
3、在 app控制器里面引入以下 user的service 然后 注入以下,直接调用函数即可
ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { UserService2 } from './user/user.service2';
// @Controller()
@Controller('get') // 相当于 路由前缀
export class AppController {
constructor(
private readonly appService: AppService,
private readonly UserService2: UserService2,
) {}
// @Get()
@Get('hello') //路由地址
getHello(): string {
// 使用 user 模块的 service
let abc = this.UserService2.getLook();
console.log(abc);
return this.appService.getHello();
}
}
6.2 全局模块
1、先创建一个 config 模块
ts
import { Global, Module } from '@nestjs/common';
@Global() // 定义为全局模块
@Module({
providers: [
{
provide: 'config',
useValue: {
baseUrl: 'http:www.baidu.com',
},
},
],
// 全局模块也要导出一遍
exports: [
{
provide: 'config',
useValue: {
baseUrl: 'http:www.baidu.com',
},
},
],
})
export class ConfigModule {}
2、在 app模块
中,导入这个 config 模块
3、在任何一个控制器中使用, 使用方法类似于5.2 章节
ts
import { Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';
import { UserService2 } from './user/user.service2';
// @Controller()
@Controller('get') // 相当于 路由前缀
export class AppController {
constructor(
private readonly appService: AppService,
private readonly UserService2: UserService2,
@Inject('config') private readonly config: any,
) {}
// @Get()
@Get('hello') //路由地址
getHello(): string {
// 使用 user 模块的 service
let abc = this.UserService2.getLook();
console.log(abc);
console.log('全局模块的注入', this.config);
return this.appService.getHello();
}
}
6.3 动态模块
1、增加一个静态方法
ts
// config.module.ts
import { Module, DynamicModule, Global } from '@nestjs/common';
interface Options {
path: string;
}
@Global()
@Module({})
export class ConfigModule {
static forRoot(options: Options): DynamicModule {
return {
module: ConfigModule,
providers: [
{
provide: 'config',
useValue: { baseApi: 'http:www.baidu.com' + options.path },
},
],
exports: [
{
provide: 'config',
useValue: { baseApi: 'http:www.baidu.com' + options.path },
},
],
};
}
}
2、修改 imports 导入的全局模块
ts
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DemoController } from './demo/demo.controller';
import { DemoModule } from './demo/demo.module';
import { UserModule } from './user/user.module';
import { ListModule } from './list/list.module';
import { ConfigModule } from './config/config.module';
@Module({
// imports: [DemoModule, UserModule, ListModule, ConfigModule],
imports: [
DemoModule,
UserModule,
ListModule,
ConfigModule.forRoot({
path: '/ddg',
}),
],
controllers: [AppController, DemoController],
providers: [AppService],
})
export class AppModule {}
7、中间件
中间件是在
路由处理程序 之前
调用的函数。 中间件函数可以访问请求和响应对象
中间件函数可以执行以下任务:
- 执行任何代码。
- 对请求和响应对象进行更改。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
- 如果当前的中间件函数
没有结束请求-响应周期
, 它必须调用 next()
将控制传递给下一个中间件函数。否则, 请求将被挂起。
执行顺序是 先全局中间件
=>局部中间件
布局中间件需要类,全局直接一个函数即可
7.1 局部中间件
ts
nest g mi logger // 生成一个 中间件
ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('路由拦截器嘿嘿嘿,我是局部中间件');
// res.send('拦截你');
next();
}
}
ts
import {
Logger,
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { UserService } from './user.service';
import { UserService2 } from './user.service2';
import { UserController } from './user.controller';
import { LoggerMiddleware } from 'src/logger/logger.middleware';
@Module({
controllers: [UserController],
// providers: [UserService], // 这个是简写
providers: [
{
provide: 'ddgService',
useClass: UserService,
},
{
provide: 'ddgService2',
useValue: ['呆呆狗1号', '呆呆狗2号', '呆呆狗3号'],
},
UserService2,
{
provide: 'ddgService3',
inject: [UserService2],
useFactory: (UserService2: UserService2) => {
console.log(UserService2.getSay());
return 123;
},
},
],
exports: [UserService2],
})
// export class UserModule {}
export class UserModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// 拦截这个控制器的接口
// consumer.apply(LoggerMiddleware).forRoutes('user');
// 拦截这个控制器下的 指定 格式的接口
consumer.apply(LoggerMiddleware).forRoutes({
method: RequestMethod.GET,
path: '/user',
});
// 拦截所有的
// consumer.apply(LoggerMiddleware).forRoutes(UserController);
}
}
user,module 使用中间件
7.2 全局中间件
ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
function middleWareAll(req, res, next) {
console.log('我是全局中间件');
next();
}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 注册全局中间件
app.use(middleWareAll);
app.enableCors(); // 跨域的
// 处理接口版本
// 然后再控制器里面 开启版本号
app.enableVersioning({
type: VersioningType.URI,
});
await app.listen(3000);
}
bootstrap();
8、上传图片及资源目录
shell
# 安装这俩包
multer @types/multer
新建一个 upload 模块
修改modules
ts
import { Module } from '@nestjs/common';
import { UploadService } from './upload.service';
import { UploadController } from './upload.controller';
import { MulterModule } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { join, extname } from 'path';
@Module({
imports: [
MulterModule.register({
// 配置图片存放的位置
storage: diskStorage({
destination: join(__dirname, '../images'),
filename: (req, file, cb) => {
console.log('文件信息', file);
const filename = `${Date.now()}-${extname(file.originalname)}`;
return cb(null, filename);
},
}),
}),
],
controllers: [UploadController],
providers: [UploadService],
})
export class UploadModule {}
修改controller
ts
import {
Controller,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
@Controller('upload')
export class UploadController {
constructor(private readonly uploadService: UploadService) {}
@Post()
// 单个文件就是 FileInterceptor,参数就是 对象的key
// 多个文件就是,FilesInterceptor, 区别就是file是后面有没有s
@UseInterceptors(FileInterceptor('file'))
upload(@UploadedFile() file) {
return {
state: '1',
data: {
file,
},
};
}
}
开启资源目录
ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
function middleWareAll(req, res, next) {
console.log('我是全局中间件');
next();
}
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 注册全局中间件
app.use(middleWareAll);
app.enableCors(); // 跨域的
// 处理接口版本
// 然后再控制器里面 开启版本号
app.enableVersioning({
type: VersioningType.URI,
});
// 配置静态资源访问目录
app.useStaticAssets(join(__dirname, 'images'), {
prefix: '/file',
});
await app.listen(3000);
}
bootstrap();
9、下载图片
10、拦截器
在公司的后端接口,都是标准格式的,比如下图。一般都是有一个标准的json格式
在src 下,创建 utils 文件夹,然后创建 responseIntercept.ts 文件
ts
// src/utils/responseIntercept.ts
import {
Injectable,
NestInterceptor,
CallHandler,
ExecutionContext,
} from '@nestjs/common';
import { map } from 'rxjs/operators';
import type { Observable } from 'rxjs';
interface data<T> {
data: T;
}
@Injectable()
export class ResponseIntercept<T = any> implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<data<T>> {
return next.handle().pipe(
map((data) => {
return {
data, // 这里的data 就是控制器,return的东西
state: '1',
mssage: 'hello 呆呆狗',
};
}),
);
}
}
然后在 main.ts 里面引入
ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { ResponseIntercept } from './utils/responseIntercept';
function middleWareAll(req, res, next) {
console.log('我是全局中间件');
next();
}
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 注册全局中间件
app.use(middleWareAll);
app.enableCors(); // 跨域的
// 处理接口版本
// 然后再控制器里面 开启版本号
app.enableVersioning({
type: VersioningType.URI,
});
// 配置静态资源访问目录
app.useStaticAssets(join(__dirname, 'images'), {
prefix: '/file',
});
// 注册全局 请求响应拦截器
app.useGlobalInterceptors(new ResponseIntercept());
await app.listen(3000);
}
bootstrap();
11、异常拦截器
在 src / utils 文件夹下,创建 errorIntercept.ts 文件
ts
// src/utils/errorIntercept.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import type { 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();
// console.log('status', status, request., response);
response.status(status).json({
data: exception.message,
time: new Date().getTime(),
success: false,
path: request.url,
status,
});
}
}
ts
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { ResponseIntercept } from './utils/responseIntercept';
import { HttpFilter } from './utils/errorIntercept';
function middleWareAll(req, res, next) {
console.log('我是全局中间件');
next();
}
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 注册全局中间件
app.use(middleWareAll);
app.enableCors(); // 跨域的
// 处理接口版本
// 然后再控制器里面 开启版本号
app.enableVersioning({
type: VersioningType.URI,
});
// 配置静态资源访问目录
app.useStaticAssets(join(__dirname, 'images'), {
prefix: '/file',
});
// 全局 异常拦截器
app.useGlobalFilters(new HttpFilter());
// 注册全局 请求响应拦截器
app.useGlobalInterceptors(new ResponseIntercept());
await app.listen(3000);
}
bootstrap();
当 访问一个不存在的路由的时候 ,就会报以下错误
12、内置管道
Nest
自带九个开箱即用的管道,即
ValidationPipe
ParseIntPipe
ParseFloatPipe
ParseBoolPipe
ParseArrayPipe
ParseUUIDPipe
ParseEnumPipe
DefaultValuePipe
ParseFilePipe
他们从 @nestjs/common
包中导出。
ts
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
return this.listService.findOne(+id);
}
如若请求,http://127.0.0.1:3000/list/2 ,接口返回值
json
{
"data": "Validation failed (uuid is expected)",
"time": 1718319105042,
"success": false,
"path": "/list/2",
"status": 400
}
因为他需要一个guid
13、管道验证
先生成一个模块
nest g res pipeValidate
就会在 src 文件夹下看到以下目录
然后再这个文件夹下,生成一个 验证管道
nest g pi validate
ts
// ./validate/validate.pipe
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
@Injectable()
export class ValidatePipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
console.log('管道验证', value, metadata);
return value;
}
}
然后对 控制器文件的 某个接口,增加管道验证
需要先导入 ./validate/validate.pipe ,然后 传递给某个接口的,
ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { PipeValidateService } from './pipe-validate.service';
import { CreatePipeValidateDto } from './dto/create-pipe-validate.dto';
import { UpdatePipeValidateDto } from './dto/update-pipe-validate.dto';
// 管道验证
import { ValidatePipe } from './validate/validate.pipe';
@Controller('pipe-validate')
export class PipeValidateController {
constructor(private readonly pipeValidateService: PipeValidateService) {}
@Post()
// create(@Body() createPipeValidateDto: CreatePipeValidateDto) {
// return this.pipeValidateService.create(createPipeValidateDto);
// }
create(@Body(ValidatePipe) createPipeValidateDto: CreatePipeValidateDto) {
return this.pipeValidateService.create(createPipeValidateDto);
}
}
用在 body 后,测试一下这个接口,接口传递
{
"name":"呆呆狗",
"age": 20
}
然后 验证器的ts文件, 打印 console.log('管道验证', value, metadata); 结果如下
管道验证 { name: '呆呆狗', age: 20 } {
metatype: [class CreatePipeValidateDto],
type: 'body',
data: undefined
}
value 就是 控制器里面解析的body的值, metadata 就是这个 类, type 是 控制器里面,用 这个管道验证,前面装饰器的名称。 data 就是 装饰器的 key 名称
控制器如若改成 @Body('name', ValidatePipe) 那么输出就是以下结果
管道验证 呆呆狗 { metatype: [class CreatePipeValidateDto], type: 'body', data: 'name' }
安装验证器
shell
npm i --save class-validator class-transformer
修改验证器 metadata的 metatype 的class 对应的文件,就是
dto/create-pipe-validate.dto.ts
ts
// dto/create-pipe-validate.dto.ts
import { IsNotEmpty, IsString, Length, IsNumber } from 'class-validator';
export class CreatePipeValidateDto {
@IsNotEmpty() //验证是否为空
@IsString() //是否为字符串
@Length(3, 10, {
message: 'name的长度在3到10之间',
})
name: string;
@IsNotEmpty()
@IsNumber()
age: number;
}
ts
// validate.pipe.ts
import {
ArgumentMetadata,
HttpException,
HttpStatus,
Injectable,
PipeTransform,
} from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
@Injectable()
export class ValidatePipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
// console.log('管道验证', value, metadata);
const dto = plainToInstance(metadata.metatype, value);
const errors = await validate(dto);
console.log('errors', errors);
if (errors.length) {
throw new HttpException(errors, HttpStatus.BAD_REQUEST);
}
return value;
}
}
// 如若 前端传递参数是 { "name":'呆呆',"age":"" } ,则有以下报错
// errors [
// ValidationError {
// target: CreatePipeValidateDto { name: '呆呆', age: '' },
// value: '呆呆',
// property: 'name',
// children: [],
// constraints: { isLength: 'name的长度在3到10之间' }
// },
// ValidationError {
// target: CreatePipeValidateDto { name: '呆呆', age: '' },
// value: '',
// property: 'age',
// children: [],
// constraints: { isNotEmpty: 'age should not be empty' }
// }
// ]
// 其实就是 返回一个数组,数组每一项的类型是 ValidationError
因为前面加了异常拦截器,所以会按照一定格式输出 。但是要把这里修改以下
全局 管道验证
这里换一下
修改 main.ts
ts
// 增加以下代码
import { ValidationPipe } from '@nestjs/common';
// 注册全局 管道验证
app.useGlobalPipes(new ValidationPipe());
全局拦截器输出示例
ts
{
"data": {
"response": {
"message": [
"name的长度在3到10之间",
"age must be a number conforming to the specified constraints"
],
"error": "Bad Request",
"statusCode": 400
},
"status": 400,
"options": {},
"message": "Bad Request Exception",
"name": "BadRequestException"
},
"time": 1718492535054,
"success": false,
"path": "/pipe-validate/",
"status": 400
}
14、爬虫
- cheerio: 是jquery核心功能的一个快速灵活而又简洁的实现,主要是为了用在服务器端需要对DOM进行操作的地方,让你在服务器端和html愉快的玩耍。
- axios 网络请求库可以发送http请求
15、守卫(guard)
守卫有一个单独的责任。它们根据运行时出现的某些条件(例如权限,角色,访问控制列表等)来确定给定的请求是否由路由处理程序处理。这通常称为授权。在传统的 Express 应用程序中,通常由中间件处理授权(以及认证)。中间件是身份验证的良好选择,因为诸如 token 验证或添加属性到 request 对象上与特定路由(及其元数据)没有强关联。
守卫在每个中间件之后执行,但在任何拦截器或管道之前执行
先创建一个模块
shell
nest g res guard
# 然后创建 模块的守卫
nest g gu [name]
全局路由守卫的使用方式
ts
// main.ts 文件
import { RoleGuard } from './guard/role/role.guard';
// 全局路由守卫
app.useGlobalGuards(new RoleGuard());
局部路由守卫的使用方式
ts
// guard.controller.ts 文件
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
UseGuards,
} from '@nestjs/common';
import { GuardService } from './guard.service';
import { CreateGuardDto } from './dto/create-guard.dto';
import { UpdateGuardDto } from './dto/update-guard.dto';
import { RoleGuard } from './role/role.guard';
@Controller('guard')
export class GuardController {
constructor(private readonly guardService: GuardService) {}
@Get()
@UseGuards(RoleGuard)
findAll() {
return this.guardService.findAll();
}
}
先用 局部的继续往下
ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
UseGuards,
SetMetadata,
} from '@nestjs/common';
import { GuardService } from './guard.service';
import { CreateGuardDto } from './dto/create-guard.dto';
import { UpdateGuardDto } from './dto/update-guard.dto';
import { RoleGuard } from './role/role.guard';
@Controller('guard')
export class GuardController {
constructor(private readonly guardService: GuardService) {}
// 加以下 这个信息,
@Get()
@UseGuards(RoleGuard)
@SetMetadata('role', ['admin'])
findAll() {
return this.guardService.findAll();
}
}
ts
// role.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import type { Request } from 'express';
@Injectable()
export class RoleGuard implements CanActivate {
constructor(private Reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const admin = this.Reflector.get<string[]>('role', context.getHandler());
const request = context.switchToHttp().getRequest<Request>();
if (admin.includes(request.query.role as string)) {
return true;
} else {
return false;
}
}
}
没有权限的话就直接触发,
全局异常拦截器
了
16、自定义装饰器
nest.js 是可以自定义装饰器
shell
nest g d [name]
自定义权限装饰器
ts
// role.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const Role = (...args: string[]) => SetMetadata('role', args);
ts
@Get()
@UseGuards(RoleGuard)
// @SetMetadata('role', ['admin'])
@Role('admin')
findAll() {
return this.guardService.findAll();
}
自定义参数装饰器
ts
import { SetMetadata,createParamDecorator,ExecutionContext ,applyDecorators } from '@nestjs/common';
import type {Request} from 'express'
export const ReqUrl = createParamDecorator((data:string,ctx:ExecutionContext)=>{
const req = ctx.switchToHttp().getRequest<Request>()
return req.url
})
ts
@Get()
@UseGuards(RoleGuard)
// @SetMetadata('role', ['admin'])
@Role('admin')
findAll(@ReqUrl('123') url) {
console.log('url', url, '自定义参数装饰器读取到的');
return this.guardService.findAll();
}
17、nestjs链接数据库(typeOrm)
shell
npm install --save @nestjs/typeorm typeorm mysql2
- 修改app.module.ts 文件
主要是要引入 typeorm ,然后配置数据库的连接串及其他信息
ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DemoController } from './demo/demo.controller';
import { DemoModule } from './demo/demo.module';
import { UserModule } from './user/user.module';
import { ListModule } from './list/list.module';
import { ConfigModule } from './config/config.module';
import { UploadModule } from './upload/upload.module';
import { PipeValidateModule } from './pipe-validate/pipe-validate.module';
import { GuardModule } from './guard/guard.module';
import { TestdatabaseModule } from './testdatabase/testdatabase.module';
// 引入 typeorm
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
// imports: [DemoModule, UserModule, ListModule, ConfigModule],
imports: [
DemoModule,
UserModule,
ListModule,
ConfigModule.forRoot({
path: '/ddg',
}),
UploadModule,
PipeValidateModule,
GuardModule,
TestdatabaseModule,
TypeOrmModule.forRoot({
type: 'mysql', //数据库类型
username: 'root', //账号
password: '123456', //密码
host: 'localhost', //host
port: 3306, //
database: 'spe', //库名
entities: [__dirname + '/**/*.entity{.ts,.js}'], //实体文件
synchronize: true, //synchronize字段代表是否自动将实体类同步到数据库
retryDelay: 500, //重试连接数据库间隔
retryAttempts: 10, //重试连接数据库的次数
autoLoadEntities: true, //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
}),
],
controllers: [AppController, DemoController],
providers: [AppService],
})
export class AppModule {}
-
用 nest g res testdatabase 创建模块
-
修改实体文件
entities/testdatabase.entity.ts
ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class test {
// test 就是表名称
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
password: string;
}
- 修改
testdatabase.module.ts
导入这个实体
ts
import { Module } from '@nestjs/common';
import { TestdatabaseService } from './testdatabase.service';
import { TestdatabaseController } from './testdatabase.controller';
import { test } from './entities/testdatabase.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [TypeOrmModule.forFeature([test])],
controllers: [TestdatabaseController],
providers: [TestdatabaseService],
})
export class TestdatabaseModule {}
这样重新运行以下,就会创建 test 表
18、curd
实现创建 用户
- 修改
testdatabase.service.js
ts
import { Injectable } from '@nestjs/common';
import { CreateTestdatabaseDto } from './dto/create-testdatabase.dto';
import { UpdateTestdatabaseDto } from './dto/update-testdatabase.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { test as User } from './entities/testdatabase.entity';
import { Repository } from 'typeorm';
@Injectable()
export class TestdatabaseService {
constructor(
@InjectRepository(User) private readonly userRecord: Repository<User>,
) {}
async create(createTestdatabaseDto) {
const userRecord = new User();
userRecord.name = createTestdatabaseDto.name;
userRecord.password = createTestdatabaseDto.password;
return await this.userRecord.save(userRecord);
}
findAll() {
return `This action returns all testdatabase`;
}
findOne(id: number) {
return `This action returns a #${id} testdatabase`;
}
update(id: number, updateTestdatabaseDto: UpdateTestdatabaseDto) {
return `This action updates a #${id} testdatabase`;
}
remove(id: number) {
return `This action removes a #${id} testdatabase`;
}
}
这样刷新以下表,就可以看到了