记录 NestJS 学习

171 阅读8分钟

NestJS

eslint报错 'cr'

eslintrc.js  
rules:[
      ...
      "prettier/prettier": ["error", { "endOfLine": "auto" }]
]

1.ioc 依赖注入

// 依赖注入
class A {
    name: string
    constructor(name: string) {
        this.name = name
    }
}
/* 

// 传统方式 获取 new A 耦合度太高
// 当引入别的库时,不太适合再次封装
class B{
    a:any
    constructor(){
        this.a = new A().name
    }
} 
*/

// 定义一个B类,给类中的mo对象,给对象添加class
class B {
    mo: any
    constructor() {
        this.mo = {}
    }

    provide(key: string, mo: any) {
        this.mo[key] = mo
    }
    get(key: string) {
        return this.mo[key]
    }
}

const b = new B()
b.provide('A', new A("陆小杭"))
b.provide('B', new A("陆大杭"))


class C {
    a: any
    b: any
    constructor(b: B) {
        this.a = b.get("A")
        this.b = b.get("B")
    }

    get() {
        console.log(this.a);

    }
}

new C(b).get()

2.修饰器

// 1.类装饰器  ClassDecorator
// 2.方法装饰器 MethodDecorator(参数1:原型对象,参数2:函数名称,参数3:PropertyDescriptor) 
// 3.参数装饰器 ParameterDecorator 参数修饰器比方法修饰器更早执行
// 4.属性装饰器 PropertyDecorator 
// 5.装饰器工厂
// 6.import "reflect-metadata"
// 7.axios
// 8.Reflect.metadata 元数据
/* 
    元数据的添加 Reflect.metadata
    Reflect.metadata(参数1:对象上添加的key,参数2:对象添加的值,参数3:添加元数据的类) //给参数3的类,添加 key:value 元数据
    Reflect.metadata(a,"123",obj)  // obj{a:"123"}

    Reflect.metadata(参数1:对象上添加的key,参数2:对象添加的值,参数3:添加元数据的类,参数4:添加元数据的属性)  //给参数3的类的参数4属性,添加 key:value 元数据
    Reflect.metadata(a,"123",obj,a)  // obj{ a:{ a:"123"}}

    元数据获取 Reflect.getMethod
    Reflect.getMethod(参数1:对象上的key,参数2:元数据的类) //获取 参数2类 挂载的元数据
    Reflect.getMethod(参数1:对象上的key,参数2:元数据的类,参数3:元数据参数2类的属性) //获取 参数2类.参数3 的元数据
*/

import axios from 'axios';
import 'reflect-metadata'

// 1.类装饰器  ClassDecorator
const create:ClassDecorator = (target) =>{
    target.prototype.lu = "luxiaohang"
    target.prototype.fn = ()=>{
        console.log("桀桀桀");
    }
}
// 有了装饰器后,可以不破坏原本class结构去添加属性
@create 
class People{
}
const man = new People() as any
console.log(man.lu);

// 通过函数科里化,用装饰器给工厂直接传参
const create2  = (name:string)=>{
    const fn:ClassDecorator = (target:any)=>{
        // 类原型上挂载属性
        target.prototype.name = name

        target.prototype.fn =()=>{
            console.log('base2 装饰器');
        }
    }
    return fn
}

@create2("lxh")
class People2 {
}
// 实例化装饰器对象,需要类型断言因为类型默认为:ClassDecorator
const man2 = new People2() as any
console.log(man2.name);

//===================下面方法在一个类中使用=======================
//类装饰器
const Base = (name:string)=>{
    // target 传进来的是 class 
    const fn:ClassDecorator = (target)=>{
        target.prototype.name = "张三干掉" + name
        target.prototype.fn = ()=>{
            console.log("我是base fn()");
        }
    }
    return fn
}


// 2.方法装饰器
const get = (url:string)=>{
    /* 
    // 装饰器形参
   fn:MethodDecorator =(
        target:"获得class中所有方法", // { getList: [Function (anonymous)], create: [Function (anonymous)] }
        propertyKey:"获得当前方法名", //  getList
        descriptor:"获得一个对象 .value给方法回传的内容"{value: [Function (anonymous)],writable: true,enumerable: true,configurable: true}
    ) 
    */
    //descriptor需要给定PropertyDescriptor 属性,不然参数不可用
    const fn:MethodDecorator =(target,propertyKey,descriptor:PropertyDescriptor)=>{
        // 获取到字符串 list
        const key = Reflect.getMetadata('getList', target)
            axios.get(url)
            .then(res => {
                // 通过 descriptor 回传成功的回调.返回元数据 value
            
                descriptor.value(res.data[key])
            })
    }
    return fn
}

// 3.参数装饰器
const List = ()=>{
    // 参数修饰器需要加类型 ParameterDecorator 。参数修饰器
    const fn:ParameterDecorator = (target,key,index)=>{
        //设置一个 getList 值为 list 供元数据获取
        Reflect.defineMetadata("getList","list",target)
    }
    return fn
}

// 4.属性修饰器
const Name:PropertyDecorator = (target,propertyKey)=>{
        console.log(target, propertyKey);   //{},name
}

@Base('LXH')
class Http{
    // 4.属性修饰器
    @Name
    name:string
    constructor(){
        this.name = "张三"
    }

    // 2.指定一个方法修饰器
    @get('http://localhost:8080')
    // 3.指定一个参数修饰器
    getList(@List() data:any){
        console.log("我收到了后端请求",data);
    }
    create(){
    }
}


3.nest.js 指令

//安装全局环境
pnpm i -g @nestjs/cli 

//nest指令创建项目
nest new 项目名

//创建一个 module.ts 模块
nest g mo 模块名称

//创建一个 service.ts 模块
nest g s 模块名称

//直接生成一个 crud 
 nest g resource 模块名称


img

4.nest 路由

import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UserService } from './user.service';
import {
  Controller,
  Get,
  Request,
  Response,
  Query,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';

@Controller('user')
export class UserController {
   //这里相当于 new userService
  constructor(private readonly userService: UserService) {}

  @Get()
  // 获取Query中的单个属性
  findAll(@Query('name') query) {
    console.log(query);

    return {
      code: 200,
    };
  }
  // 读取动态路由
  @Get(':id')
  finduserId(@Request() req) {
    console.log(req.params);
    return {
      code: 200,
      data: req.params,
    };
  }
  // 创建 post请求
  @Post()
  creat(@Request() req) {
    console.log(req.body);

    if (req.query.name !== '张三') {
      return {
        code: '200',
      };
    }
  }
}


//添加全局路由前缀  在main文件中
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 添加全局路由前缀 api
  app.setGlobalPrefix('api');
  await app.listen(3000);
}


5.依赖注入

//依赖 providers 用法 module 层
import { Module } from '@nestjs/common';
import { GirlController } from './girl.controller';
import { GirlService } from './girl.service';

@Module({
  controllers: [GirlController],
  providers: [
    GirlService,
    // 定义类
    {
      provide: 'getgirl',
      useClass: GirlService,
    },
    // 定义值
    {
      provide: 'getnumber',
      useValue: '123456',
    },
    // 定义工厂
    {
      provide: 'MyFactory',
      useFactory() {
        console.log('Factory');
        return 'fact func()';
      },
    },
  ],
})
export class GirlModule {}



//依赖 providers 用法 controller 层
import { Controller, Get, Inject, Post, Req, Res } from '@nestjs/common';
import { GirlService } from './girl.service';

@Controller('girl')
export class GirlController {
  constructor(
    // 服务层挂载
    private girlService: GirlService,
    // 通过inject 挂载
    @Inject('getnumber') private nb: string,
    @Inject('getgirl') private girl: GirlService,
    @Inject('MyFactory') private factory: string,
  ) {}
  @Get(':id')
  getGrils(@Req() req) {
    const id: number = parseInt(req.params.id);
    return this.girlService.getInfo(id);
  }

  @Get('/u/info')
  getInfo(@Req() req) {
    //通过依赖注入获取 server
    return this.girl.getInfo(1);
  }

  @Get('/u/number')
  getNumber(@Res() res) {
    // 获取注入工厂
    console.log(this.factory);
    // 获取注入number
    res.send(this.nb);
  }

  @Post('')
  postlove() {
    return {
      code: 200,
      msg: '我是girl',
    };
  }
}



6.数据库连接

nest.js连接数据库

1.安装@nestjs/typeorm

安装nest.js提供的typeorm包,因为它是可用于Typescript的最成熟的对象关系映射器(ORM),由于他是Typescript编写的,因此可以很好的和Nest框架集成。


$ pnpm install --save @nestjs/typeorm typeorm

2.TypeOrmModule导入AppModule

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
    imports: [
        TypeOrmModule.forRoot({
            type: 'mysql',  // 数据库类型
            host: 'localhost', // 域名
            port: '3306',     // 端口
            username: 'root', // 数据库名字
            password: '123456',  // 数据库密码
            database: 'test',    // 库名
            entities: [User],   // 导入的实体(数据库模型)
            synchronize: true,
            retryAttempts: 100,
            // 重试连接数据库的次数,默认为10次
            retryDelay: 2000,
            // 两次重试连接的间隔(ms),默认为3000
            autoLoadEntities: false
            // 如果为true,将自动加载实体(默认:false)
        })
    ]
})

forRoot()方法支持TypeORM包中createConnection()函数暴露出的配置属性

7.中间件

1.局部中间件

/**
	局部中间件的使用
	1.创建中间件文件
	2.module层导入中间件
	3.拦截操作
	4.拦截某一类型路由
	
*/


// 1.中间件文件创建
nest g mi '创建名'

//init.middlexxxware.ts 中间件内容
import { Injectable, NestMiddleware } from '@nestjs/common';
//局部中间件
@Injectable()
export class InitMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    //使用中间件
    console.log('局部中间件Init');
    next();
  }
}
//拦截中间件
@Injectable()
export class InterCept implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    //使用中间件
    console.log('USER的所有get被我拦截中间件承包了 桀桀桀');
    res.send('USER的所有get被我拦截中间件承包了 桀桀桀');
  }
}


//2.module层导入中间件
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

// 引入局部中间件
import { InitMiddleware } from '../init/init.middleware';

@Module({
  controllers: [UserController],
  providers: [UserService],
})
// 实现 NestModule 接口
export class UserModule implements NestModule {
   //中间件实例化
  configure(consumer: MiddlewareConsumer) {
    //consumer.apply(添加的中间件).forRoutes(路由名)
    consumer.apply(InitMiddleware).forRoutes('user/jack');
    consumer.apply(InitMiddleware).forRoutes('user/g2/jntm');
  }
}

2.全局中间件

/*
	全局中间件使用
	1.外部创建中间件函数 或 定义中间件方法
	2.main中注册全局中间件
*/

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

//定义全局中间件 middleAll
function middleAll(req, res, next) {
  console.log(`全局中间件`);
  next();
}

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 注册全局中间件 middleAll
  app.use(middleAll);
  await app.listen(3000);
}
bootstrap();

8.模块化

1.单个模块的使用

//在 boy.module.ts 中导出单个模块
import { Global, Module } from '@nestjs/common';
import { BoyService } from './boy.service';
import { BoyController } from './boy.controller';

@Module({
  controllers: [BoyController],
  providers: [BoyService],
  // 导出模块
  exports: [BoyService],
})
export class BoyModule {}

//在 girl.module.ts 中导入 boy 模块
import { Module } from '@nestjs/common';
import { GirlService } from './girl.service';
import { GirlController } from './girl.controller';
import { BoyService } from '../boy/boy.service';

@Module({
  controllers: [GirlController],
  providers: [
    GirlService,
    // 导入boy模块
    BoyService,
    {
      provide: 'girl',
      useClass: GirlService,
    },
  ],
})
export class GirlModule {}

//在 girl.controller.ts 中使用 boy 模块方法
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  Inject,
} from '@nestjs/common';
import { GirlService } from './girl.service';
// 引入boy模块
import { BoyService } from '../boy/boy.service';
import { CreateGirlDto } from './dto/create-girl.dto';
import { UpdateGirlDto } from './dto/update-girl.dto';

@Controller('girl')
export class GirlController {
  constructor(
     //使用 Girl 模块
    @Inject('girl') private readonly girl: string,
    //使用 Girl 模块
    private readonly girlService: GirlService,
    private boyService: BoyService,
  ) {}

  @Get()
  findAll() {
    console.log(this.configName?.name);
    return this.boyService.findAll();
  }

}

2.全局模块的使用

//1.config.module声明模块并且导出
import { Module, Global } from '@nestjs/common';

//声明全局模块
@Global()
@Module({
  providers: [
    {
      provide: 'Config',
      useValue: { name: '张三' },
    },
  ],
  //导出模块
  exports: [
    {
      provide: 'Config',
      useValue: {
        configName: 'global',
      },
    },
  ],
})
export class ConfigModule {}

//2.app.module.ts 引入全局模块
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BoyModule } from './boy/boy.module';
import { GirlModule } from './girl/girl.module';
// 引入config
import { ConfigModule } from './config/config.module';

@Module({
  // 全局手动引入Config模块
  imports: [BoyModule, GirlModule, ConfigModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}



//3.全局模块在 girl 模块 control层的使用
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  Inject,
} from '@nestjs/common';
import { GirlService } from './girl.service';
// 引入boy模块
import { BoyService } from '../boy/boy.service';
import { CreateGirlDto } from './dto/create-girl.dto';
import { UpdateGirlDto } from './dto/update-girl.dto';

// 给定全局模块类型
type Con = {
  name: string;
  configName: string;
};

@Controller('girl')
export class GirlController {
  constructor(
    // 注入全局模块
    @Inject('Config') private configModule: Con,
  ) {}
    
  @Get()
  findAll() {
     //使用全局模块
    console.log(this.configModule?.configName);
    return this.boyService.findAll();
  }
}



9.文件处理

1.包安装

// nest上传图片用到的两个包
//multer   @nestjs/platform-express nestJs自带了
//multer   @types/multer 这两个需要安装

2.文件上传

2.配置上传module

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(_, file, callback) {
          const filename = `${
            // 拼接文件名
            new Date().getTime() + extname(file.originalname)
          }`;
          return callback(null, filename);
        },
      }),
    }),
  ],
  controllers: [UploadController],
  providers: [UploadService],
})
export class UploadModule {}

3.上传 controller

import {
  Controller,
  Post,
  Body,
  UploadedFile,
  UseInterceptors,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { CreateUploadDto } from './dto/create-upload.dto';
// 读取文件
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @Post('img')
  // 拦截器中间件
  @UseInterceptors(FileInterceptor('file'))
  // 上传图片
  create(@UploadedFile() file) {
    console.log(file, 'file');
    return 'file';
  }
}

4.配置静态资源 main

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  // 配置静态资源访问路径. prefix 浏览器请求地址
  app.useStaticAssets(join(__dirname, 'Images'), { prefix: '/static/img' });

  await app.listen(3000);
}
bootstrap();

3.文件下载


import { UploadService } from './upload.service';
import { CreateUploadDto } from './dto/create-upload.dto';
// 读取文件
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
// 配置express代码提示
import type { Response } from 'express';
import { join } from 'path';
// 压缩文件
import { zip } from 'compressing';

  // 下载接口
  @Get('export')
  downLoad(@Res() res: Response) {
    // 拼接路径
    const url = join(__dirname, '../Images/1685439451315.jpg');
    // 下载
    res.download(url);
  }

  @Get('stream')
  async downzip(@Res() res: Response) {
    const url = join(__dirname, '../Images/1685439451315.jpg');
    //图片生成zip形式
    const tarStream = new zip.Stream();
    await tarStream.addEntry(url);
    res.setHeader('Content-Type', 'application/octet-stream');
    res.setHeader('Content-Disposition', `attachment;filename=lxh`);

    tarStream.pipe(res);
  }


1.前端获取stream流

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<button class="upload">下载</button>
	</body>
	<script >
		const btn = document.querySelector('.upload')
		const useFetch = async(url)=>{
			const res = await fetch(url).then(res=>res.arrayBuffer())
			
			var a = document.createElement('el')
			a.href = URL.createObjectURL(new Blob([res],{
				type:"image/png"
			}))
			a.download ='lxh.zip'
			console.log(a.download);
			a.click()
		}
		
		
		btn.onclick = (e)=>{
			useFetch('http://127.0.0.1:3000/upload/stream')
			
			// window.open('http://127.0.0.1:3000/upload/export')
		}		
	</script>
</html>

10.Rx.js

1.引入rx js

//引入 Rx.js
$ npm i rxjs

import { of, Observable, interval, take } from 'rxjs';
import { map, filter, findIndex, reduce, retry } from 'rxjs/operators';

import { of, Observable, interval, take } from 'rxjs';
import { map, filter, findIndex, reduce, retry } from 'rxjs/operators';

// 类似迭代器对象
const observable = new Observable((subscribe) => {
  subscribe.next(1);
  subscribe.next(2);
  subscribe.next(3);
  setTimeout(() => {
    subscribe.next(4);

    // 结束
    subscribe.complete();
  }, 3000);
  subscribe.next(5);
});

// 获取返回值
observable.subscribe({
  next: (cb) => {
    console.log(cb); //1 2 3 5 4
  },
});

// 间隔多少毫秒执行
interval(1)
  //到多少数字结束
  .pipe(take(309))
  //回调
  .subscribe((e) => {
    console.log(e);
  });

const sub = interval(20)
  .pipe(
    map((v) => ({
      num: v,
    })),
  )
  //手动停止
  .subscribe((e) => {
    console.log(e);

    if (e.num == 100) {
      sub.unsubscribe(); // 1,2,3,...,98,99,100
    }
  });

//指定数据
of(1, 2, 3, 4, 5, 6)
  .pipe(
    retry(3),
    map((v) => ({
      num: v,
    })),
    // 过滤
    filter((v) => v.num % 2 == 0),
  )
  .subscribe((e) => {
    console.log(e); //2,4,6
  });

11.拦截器

1.全局拦截

//common 文件夹创建一个拦截器 request.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> implements NestInterceptor {
  // 返回的 Data 类型的数据
  intercept(context, next: CallHandler): Observable<Data<T>> {
    // 返回拦截器
    return next.handle().pipe(
      map((data) => {
        return {
          data,
          status: 0,
          message: '666',
          success: '登录成功',
        };
      }),
    );
  }
}

// main.ts 中全局注册拦截器
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();


2.异常拦截

// 异常拦截器
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';

import { Request, response, Response } from 'express';

//异常拦截这里是catch
@Catch()
export class HttpFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
      //获取 req res
    const ctx = host.switchToHttp();
    const req = ctx.getRequest<Request>();
    const res = ctx.getResponse<Response>();

    console.log(req, res);

    // 接口状态信息
    const status = exception.getStatus();
    // const next = ctx.getNext();
     //返回的数据
    res.status(status).json({
      success: false,
      code: status,
      time: new Date(),
      data: exception.message,
      //  报错的接口
      path: req.url,
    });
  }
}

//main中的异常拦截器
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
// 引入异常拦截器
import { HttpFilter } from './common/filter';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 注册异常拦截
  app.useGlobalFilters(new HttpFilter());

  await app.listen(3000);
}
bootstrap();


12.管道

1.管道pipe

//管道
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  ParseIntPipe,
  ParseUUIDPipe,
} from '@nestjs/common';
import { PService } from './p.service';
import { CreatePDto } from './dto/create-p.dto';
import { UpdatePDto } from './dto/update-p.dto';

@Controller('p')
export class PController {
  constructor(private readonly pService: PService) {}

  @Post()
  create(@Body() createPDto: CreatePDto) {
    return this.pService.create(createPDto);
  }

  @Get()
  findAll() {
    return this.pService.findAll();
  }

  @Get(':id')
    //接收一个 ParseUUIDPipe 类型的参数
  findOne(@Param('id', ParseUUIDPipe) id: string) {
    console.log(typeof id, '---------------');

    return this.pService.findOne(+id);
  }
}


2.验证validate

1.安装两个插件

 $ pnpm i --save class-validator class-transformer

2.校验模块

校验规则
//规则检验模块 login.service.ts

import { IsNotEmpty, IsString, Length, IsNumber } from 'class-validator';
//规则校验
export class CreateLoginDto {
  @IsNotEmpty()
  @IsString()
  @Length(2, 10, {
    message: 'name长度不能小于或,超过10个字符',
  })
  name: string;
  @IsNumber({}, { message: 'age请输入number类型' })
  age: number;
}


校验验证模块

//配置Body模块验证文件 login.pipe.ts
import {
  ArgumentMetadata,
  HttpException,
  HttpStatus,
  Injectable,
  PipeTransform,
} from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
// 引入验证模块
import { validate } from 'class-validator';

@Injectable()
export class LoginPipe implements PipeTransform {
  // 获取数据原信息 //value = {name:xxx,age:xxx,xxx} ,metadata = {}
  async transform(value: any, metadata: ArgumentMetadata) {
    // console.log(value, metadata); 
    // plainToInstance 将值反射到类上面
    const DTO = plainToInstance(metadata.metatype, value);

    // 失败校验 返回的失败类型的数组 信息,类型是:promise。 数组为空则校验成功
    // const errors = await Validate(DTO)
    const err = await validate(DTO);
    if (err.length) {
      // 抛出一个异常失败的消息( err 失败内容 , HttpStatus:是一个 RESTFUL规则的枚举)
      throw new HttpException(err, HttpStatus.BAD_REQUEST);
    } else {
      console.log('validation succeed');
    }
    return value;
  }
}


路由引入校验
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { LoginService } from './login.service';
import { CreateLoginDto } from './dto/create-login.dto';
import { UpdateLoginDto } from './dto/update-login.dto';
// 引入login pipe
import { LoginPipe } from './login/login.pipe';

@Controller('login')
export class LoginController {
  constructor(private readonly loginService: LoginService) {}

  @Post()
  create(@Body(LoginPipe) createLoginDto: CreateLoginDto) {
    return '666';
  }

  @Get()
  findAll() {
    return this.loginService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.loginService.findOne(+id);
  }
}

13.守卫

路由页面

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';

// 局部守卫
@UseGuards(RoleGuard)
@Controller('guard')
export class GuardController {
  constructor(private readonly guardService: GuardService) {}

  @Get()
  // 定义权限 守卫
  @SetMetadata('role', ['admin'])
  findAll() {
    console.log('666');
    return this.guardService.findAll();
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateGuardDto: UpdateGuardDto) {
    return this.guardService.update(+id, updateGuardDto);
  }
}

守卫页面

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
import type { Request, Response } from 'express';
// 守卫
@Injectable()
export class RoleGuard implements CanActivate {
  //  Reflector 挂载到属性上
  constructor(private Reflector: Reflector) {}
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    // 通过Reflector获取,路由上定义的守卫属性
    const admin = this.Reflector.get<string[]>('role', context.getHandler());

    // 获取get请求传入的query参数
    const req = context.switchToHttp().getRequest<Request>();
    const role = req.query.role;

    // 对比query 和 守卫参数
    if (admin.includes(role as string)) {
      return true;
    }

    return false;
  }
}

14.自定义修饰器

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';

import { Role, User } from './role/role.decorator';

// 局部守卫
@Controller('guard')
@UseGuards(RoleGuard)
export class GuardController {
  constructor(private readonly guardService: GuardService) {}

  @Get()
  // 定义权限 守卫
  @SetMetadata('role', ['admin'])
  findAll() {
    return 'is admin';
  }

  @Get('zidingyi')
  @Role('jack')
  getjack(@User('mid') url: string) {
    console.log('url', url);
    return 'is jack';
  }

  @Get(':id')
  // 自定义修饰器
  @Role('name')
  update(@Param('id') id: string, @Body() updateGuardDto: UpdateGuardDto) {
    console.log(id);
    return ':id req';
  }
}

创建自定义修饰器

$ nest g d '文件名'

自定义修饰器组件

import {
  createParamDecorator,
  SetMetadata,
  ExecutionContext,
} from '@nestjs/common';

import type { Request } from 'express';

export const Role = (...args: string[]) => SetMetadata('role', args);

// 创建一个 形参 @ReqUrl 修饰器
// 路由 => 守卫修饰器 => 守卫模块 => 参数修饰器 => 路由
export const User = createParamDecorator(
  // data:传入的参数, ctx:上下文
  (data: string, ctx: ExecutionContext) => {
    // 获取请求路径
    const req = ctx.switchToHttp().getRequest<Request>();
    console.log(req.query, '==========', data);

    // 返回参数给到路由
    return req.url;
  },
);


全局注册守卫文件

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

// 引入守卫文件
import { RoleGuard } from './guard/role/role.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 全局注册守卫文件
  // app.useGlobalGuards(new RoleGuard());
  await app.listen(3000);
}
bootstrap();