• 【前端】Nesj 学习笔记


    1、前置知识

    1.1 装饰器

    装饰器的类型

    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请求装饰器

    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 创建项目

    npm i -g @nestjs/cli
    
    nest new [项目名称]
    
    //创建完成后,目录结果如下
    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 应用实例。
      "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
    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 第二种用法,自定义名称

    1. 修改user.module.ts
    2. 修改 user.controller.ts
    // 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 {}
    
    
    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 自定义注入值

    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 {}
    
    
    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 工厂模式

    在这里插入图片描述

    // 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 '你好啊,我是工厂模式下注入的';
      }
    }
    
    
    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 {}
    
    
    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、定义一个函数

    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模块里面 导出 这个服务

    在这里插入图片描述

    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 然后 注入以下,直接调用函数即可

    在这里插入图片描述

    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 模块

    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 章节

    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、增加一个静态方法

    // 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 导入的全局模块

    // 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 局部中间件

     nest g mi logger // 生成一个 中间件
    
    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();
      }
    }
    
    
    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 全局中间件

    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、上传图片及资源目录

    # 安装这俩包
    multer   @types/multer
    

    新建一个 upload 模块

    修改modules

    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

    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,
          },
        };
      }
    }
    
    

    开启资源目录

    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 文件

    // 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 里面引入

    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 文件

    // 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,
        });
      }
    }
    
    
    // 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 包中导出。

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

    如若请求,http://127.0.0.1:3000/list/2 ,接口返回值

    {
        "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

    在这里插入图片描述

    // ./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 ,然后 传递给某个接口的,

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

    安装验证器

    npm i --save class-validator class-transformer
    

    修改验证器 metadata的 metatype 的class 对应的文件,就是 dto/create-pipe-validate.dto.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;
    }
    
    

    在这里插入图片描述

    // 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

    // 增加以下代码
    import { ValidationPipe } from '@nestjs/common';
      // 注册全局 管道验证
      app.useGlobalPipes(new ValidationPipe());
    

    全局拦截器输出示例

    {
        "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 对象上与特定路由(及其元数据)没有强关联。

    守卫在每个中间件之后执行,但在任何拦截器或管道之前执行

    先创建一个模块

    nest g res guard
    
    # 然后创建 模块的守卫
    nest g gu [name]
    

    在这里插入图片描述

    全局路由守卫的使用方式

    // main.ts 文件
    import { RoleGuard } from './guard/role/role.guard';
    
      // 全局路由守卫
      app.useGlobalGuards(new RoleGuard());
    

    局部路由守卫的使用方式

    // 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();
      }
    
    }
    
    
    

    先用 局部的继续往下

    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();
      }
    
    
    }
    
    
    // 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 是可以自定义装饰器

    在这里插入图片描述

    nest g d [name]
    

    自定义权限装饰器

    // role.decorator.ts
    import { SetMetadata } from '@nestjs/common';
    
    export const Role = (...args: string[]) => SetMetadata('role', args);
    
    
      @Get()
      @UseGuards(RoleGuard)
      // @SetMetadata('role', ['admin'])
      @Role('admin')
      findAll() {
        return this.guardService.findAll();
      }
    

    自定义参数装饰器

    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
    })
    
      @Get()
      @UseGuards(RoleGuard)
      // @SetMetadata('role', ['admin'])
      @Role('admin')
      findAll(@ReqUrl('123') url) {
        console.log('url', url, '自定义参数装饰器读取到的');
    
        return this.guardService.findAll();
      }
    

    17、nestjs链接数据库(typeOrm)

    npm install --save @nestjs/typeorm typeorm mysql2
    
    1. 修改app.module.ts 文件

    主要是要引入 typeorm ,然后配置数据库的连接串及其他信息

    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 {}
    
    
    1. 用 nest g res testdatabase 创建模块

    2. 修改实体文件 entities/testdatabase.entity.ts

    import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
    
    @Entity()
    export class test {
      // test 就是表名称
    
      @PrimaryGeneratedColumn()
      id: number;
    
      @Column()
      name: string;
    
      @Column()
      password: string;
    }
    
    
    1. 修改 testdatabase.module.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

    实现创建 用户

    1. 修改testdatabase.service.js
    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`;
      }
    }
    
    

    在这里插入图片描述

    这样刷新以下表,就可以看到了

    在这里插入图片描述

  • 相关阅读:
    前端中的身份认证
    2024级199管理类联考之数学基础(下篇)
    video_topic
    IP地址在网络安全中的关键作用
    32 道 Spring 常见面试题!万字总结!
    【PPT】NET Conf China 2022,主题:C#在iNeuOS工业互联网操作系统的开发及应用
    机器人路径规划:基于Q-learning算法的移动机器人路径规划,可以自定义地图,修改起始点,提供MATLAB代码
    github连接失败Host key verification failed.解决方案
    linux系统编程6-守护进程、线程
    CSS 基础知识 属性
  • 原文地址:https://blog.csdn.net/qq_45634593/article/details/139753828