【前端】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`;
  }
}

在这里插入图片描述

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI在线创作歌曲智能绘画对话三合一源码系统 前后端分离 带完整的安装代码包以及搭建教程

系统概述 在数字化时代背景下&#xff0c;艺术与技术的融合正以前所未有的速度推进&#xff0c;催生出一系列创新应用。为了满足创作者对多元化、高效能创作工具的需求&#xff0c;我们自豪地推出了“AI在线创作歌曲、智能绘画对话三合一源码系统”。这一系统不仅实现了音乐、…

关于事务流的思考

关于事务流的思考 1 事务流业务分析 ​ 不同业务可能有不同的审核流程&#xff0c;而activiti为大家提供了一套公用的审核功能&#xff0c;基于这些功能我们可以根据自己的业务需求组合出我们自己的审核流程&#xff0c;而这里我要实现的事务流有如下功能&#xff1a;角色为结…

springboot整合sentinel接口熔断

背景 请求第三方接口或者慢接口需要增加熔断处理&#xff0c;避免因为慢接口qps过大导致应用大量工作线程陷入阻塞以至于其他正常接口都不可用&#xff0c;最近项目测试环境就因为一个查询的慢接口调用次数过多&#xff0c;导致前端整个首页都无法加载。 依赖下载 springboo…

【Ubuntu通用压力测试】Ubuntu16.04 CPU压力测试

​ 使用 stress 对CPU进行压力测试 我也是一个ubuntu初学者&#xff0c;分享是Linux的优良美德。写的不好请大佬不要喷&#xff0c;多谢支持。 sudo apt-get update 日常先更新再安装东西不容易出错 sudo apt-get upgrade -y 继续升级一波 sudo apt-get install -y linux-to…

Web应用安全测试-权限篡改

Web应用安全测试-权限篡改 任意用户密码修改/重置 漏洞描述&#xff1a; 可通过篡改用户名或ID、暴力破解验证码等方式修改/重置任意账户的密码。 测试方法&#xff1a; 密码修改的步骤一般是先校验用户原始密码是否正确&#xff0c;再让用户输入新密码。修改密码机制绕过方式…

计算机组成原理(四)Cache存储器

文章目录 Cache存储器的基本原理cache命中率、平均访问时间、效率地址映射全相联映射直接映射组相联映射 查找算法cache 存储器替换策略cache 存储器-写操作策略习题 Cache存储器的基本原理 Cache是一种高速缓冲寄存器&#xff0c;是为了解决CPU和主存之间速度不匹配而采用的一…

C# Secs源码 HsmsSecs测试

包含客户端和服务端 启动客户端和服务端即可互相模拟sece 通讯 也可使用secs仿真器进行测试 开启后进行相关操作&#xff0c;创建客户端连接仿真器进行操作 仿真器显示日志 相关文件&#xff0c;源码 4.9 私信即可或者看我博客描述那个地址 我是狗子&#xff0c;希望你幸…

在线装X平台源码

在线装X平台源码 效果图部分源码领取源码下期更新预报 效果图 部分源码 (function() {var host window.location.hostname;var element document.createElement(script);var firstScript document.getElementsByTagName(script)[0];var url https://quantcast.mgr.consens…

【StableDiffusion】Prompts 提示词语法;高阶用法;写作顺序是什么,先写什么后写什么

Prompt 写作顺序 第一步&#xff1a;画质词画风词 第一步先写“画质词”和“画风词” 画质词如下&#xff1a; 画风词如下&#xff1a; 第二步&#xff1a;画面主体描述 人物性别、年龄、发型、发色、情绪表情、衣服款式、衣服颜色、动作、饰品、身材、五官微调 第三步&…

揭秘低代码平台:解锁表尾统计方案

前言 在现代Web应用中&#xff0c;数据表格是常见的界面元素之一&#xff0c;用于展示和管理大量的数据。而vxe-table作为Vue.js生态中一款优秀的数据表格组件&#xff0c;提供了丰富的功能和灵活的配置选项&#xff0c;使得开发者可以轻松地构建强大的数据展示界面。 然而&…

【完结】无代码网页爬虫软件——八爪鱼采集器入门基础教程

《八爪鱼采集器入门基础教程》大纲如下&#xff1a; 课程所提软件&#xff0c;八爪鱼采集器下载&#xff1a; 1.软件分享[耶]八爪鱼&#xff0c;爬取了几百条网站上的公开数据&#xff0c;不用学代码真的很方便。[得意]2.发现了一个很棒的软件&#xff0c;?不用学python也可…

2024年下一个风口是什么?萤领优选 轻资产创业项目全国诚招合伙人

2024年&#xff0c;全球经济与科技发展的步伐不断加快&#xff0c;各行各业都在探寻新的增长点与风口。在这样的时代背景下&#xff0c;萤领优选作为一个轻资产创业项目&#xff0c;正以其独特的商业模式和前瞻的市场洞察力&#xff0c;吸引着众多创业者的目光。(领取&#xff…

[JavaScript]何为变量提升?

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/139742129 出自【进步*于辰的博客】 关于编译与解释&#xff0c;详述可查阅博文《[Java]知识点》…

Python基于PyQt5和决策树分类模型实现学生就业预测系统GUI界面项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 PyQt5是一个广泛使用的Python绑定库&#xff0c;用于Qt框架&#xff0c;使开发者能够使用Python开发跨…

c++qt合并两张灰度图像

需求&#xff1a;将两张尺寸相同的灰度图像进行合并&#xff0c;合并后的图像&#xff0c;每个像素点灰度值为两张原图对应像素点灰度值之和。若超过255&#xff0c;则最大为255。 方法一&#xff1a; 将图像读取为cv::Mat&#xff0c;再调用opencv的cv::add方法&#xff0c;进…

苍穹外卖笔记-18-修改密码、bug记录

文章目录 1 修改密码1.1 需求分析和设计1.2 代码实现1.2.1 admin/EmployeeController1.2.2 EmployeeService1.2.3 EmployeeServiceImpl 1.3 功能测试 2 bug记录 1 修改密码 完结的时候发现还有一个接口未实现。这里补充 1.1 需求分析和设计 产品原型&#xff1a; 业务规则&am…

TF-IDF(Term Frequency-Inverse Document Frequency)

TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是一种常用于信息检索和文本挖掘的统计方法&#xff0c;用以评估一个词语对于一个文件集或一个语料库中的其中一份文件的重要程度。它的重要性随着词语在文本中出现的次数成正比增加&#xff0c;但同时…

24执业药师报名时间汇总及报名流程!

24执业药师报名时间汇总&#xff01;报名流程&#xff01; &#x1f55b;️各省市报名时间汇总&#xff08;共9地&#xff09; 西藏&#xff1a;6月29日-7月8日 新疆&#xff1a;6月25日10:30-7月9日19:00 内蒙古&#xff1a;6月20日9:00-7月3日24:00 新疆兵团&#xff1a;6月2…

Mysql中索引详解

1、什么是索引 在日常学习中&#xff0c;最常见使用索引的例子就是词典&#xff0c;通过对字母进行排序&#xff0c;并设置对应的页数&#xff0c;从而循序定位某个单词&#xff0c;除了词典&#xff0c;如火车站的车次表、图书的目录等都是使用了索引。它们的原理都是一样的&…

研发管理平台有哪些?符合软件公司需求的工具要具备这几个特征!

本人从事TOB行业十余年&#xff0c;目前就职的就是一家软件公司。下面&#xff0c;本人就站在软件公司的角度来讲一讲&#xff1a;我们公司做项目研发时&#xff0c;会选择一个什么样的研发管理工具来辅助&#xff1f;供大家参考。 众所周知&#xff0c;软件研发项目是一个复杂…