nest 基础学习[一] 创建控制器 Session 路由通配符 设置响应信息 Async/await 定义数据 服务 可选依赖 模块 动态模块 中间件 异常 管道 决定自己的高度的是你的态度,而不是你的才能 记得我们是终身初学者和学习者

nest 基础学习[一]
创建控制器
Session
路由通配符
设置响应信息
Async/await
定义数据
服务
可选依赖
模块
动态模块
中间件
异常
管道
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者

模块
nest g mo user
控制器
nest g co user
服务
nest g s user
@Request() req
@Res() res
@Next() next
@Session() 这个不懂
@Param(key?:string) req.params/req.params 动态id :id
@Body(key?:string) req.body/req.body[key] post参数
@Query(key?string) req.query/req.query[key] 问号参数
@Headers(name?:string) req.headers/req.headers[name] 请求头
@Ip() req.ip
  @Get('home')
  findOne(@Query() query,@Res() res): string {
    console.log(query);
    res.send('ssssss') //跟上面类似
    // return 'findOne';
  }
如果不是用Res 记得要去掉

import { Request,Response } from 'express';

@Controller('cats')
export class CatsController {
  @Get()
    			// 请求头
  findAll(@Req() request: Request): string {
    return 'This action returns all cats';
  }
}

//响应头
 @Get('home/:id')
  findOne(@Res() res: Response): string {
    res.status(HttpStatus.OK).json([]);
  }

Session

https://www.codenong.com/cs106637304/

路由通配符

* / ? / + / () [a-z]

  @Get('getThree*')
  findThree() {
    return '成功1';
  }
// 我们发现 getThree12123 也能匹配上

设置响应信息

设置状态码

@HttpCode(201)

  @Get('codeNum')
  @HttpCode(201)
  codeThree() {
    return { sex:21 };
  }

通常状态码不是固定,可以通过@Res 注入对象

设置响应头

@header() 通过 res.header()直接调用

重定向

使用 @Redirect()装饰器或特定于库的响应对象(并直接调用 res.redirect()

没思考到使用场景

子域路由

限定特别的子域名路由访问

Async/await

每个异步函数都必须返回 Promise。这意味着您可以返回延迟值, 而 Nest 将自行解析它

两种方式

@Get()
async findAll(): Promise<any[]> {
  return [];
}
==========
@Get()
findAll(): Observable<any[]> {
  return of([]);
}    

定义数据

它定义了如何通过网络发送数据。我们可以通过使用 TypeScript接口或简单的类来完成。但是我们推荐使用类

确定 DTO(数据传输对象)模式

xxx.dto.ts

export class CreateCatDto{
  readonly name:string
  readonly age:number
  readonly breed:string
}

服务

类型

还是用ts的

新建一个interface文件夹
xxx.interface.ts

export interface Cat{
  name:string,
  age:number,
  breed:string
}

可选依赖

参考angular

import { Injectable, Optional, Inject } from '@nestjs/common';

@Injectable()
export class TestTwoService {
  constructor() { }
}


constructor(@Optional() private two?:TestTwoService) {
    console.log(this.two);//null
  }

服务, 试了下,具体可以全部参考angular的写的,基本一样

模块

providers 由 Nest 注入器实例化的提供者,并且可以至少在整个模块*享
controllers 必须创建的一组控制器
imports 导入模块的列表,这些模块导出了此模块中所需提供者
exports 由本模块提供并应在其他模块中可用的提供者的子集。

nest 基础学习[一]
创建控制器
Session
路由通配符
设置响应信息
Async/await
定义数据
服务
可选依赖
模块
动态模块
中间件
异常
管道
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者

每个模块都是一个共享模块。一旦创建就能被任意模块重复使用

这个还是跟angular一样,对新手比较友好

导出

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})
导出当前模块的服务

可以这样

@Module({
  controllers: [CatsNumController],
  providers: [CatsNumService],
  exports:[CatsNumModule,CatsNumService],
})
export class CatsNumModule {}

其他模块
@Module({
  imports: [CatsNumModule], //导入当前的
  controllers: [CatsTwoController],
})
就可以使用 CatsNumService啦

共享模块

可以把很多东西放在同一个模块的,仅在需要时导入该模块

@NgModule({
 imports:      [ CommonModule ],
 declarations: [ CustomerComponent, NewItemDirective, OrdersPipe ],
 exports:      [ CustomerComponent, NewItemDirective, OrdersPipe,
                 CommonModule, FormsModule ]
})
export class SharedModule { }
把需要的导出来,然后其他模块引入这个模块就可以啦

全局模块

@Global()
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

@Global 装饰器使模块成为全局作用域。 全局模块应该只注册一次,最好由根或核心模块注册。

动态模块

如果修改成全局

{
  global: true,
  module: DatabaseModule,
  providers: providers,
  exports: providers,
}
import { Module, DynamicModule } from '@nestjs/common';

@Module({})
export class OneModule {
  static forRoot(baseConfigPath: string): DynamicModule {
    const providers = [{ provide: 'Config', useValue: baseConfigPath }];

    return {
      module: OneModule,
      providers,
      exports: providers,
    };
  }
}

模块使用

@Module({
  imports: [OneModule.forRoot('333')],
  controllers: [DatebaseController],
  ...
})
    
使用
export class DatebaseController {
  constructor(@Inject('Config') config) {
    console.log(config);//拿到啦
  }
}

中间件

nest g mi 文件名 文件夹

也可以这样

nest g mi ./xxx/xxx

xxx.middleware.ts

支持依赖注入

中间件通过constructor 注入

应用中间件

  • 新建的中间件
@Injectable()
export class LoggerOneMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log('我这是一个中间件-----------');
    next();
  }
}
  • 添加到模块中, 使用模块类的configure()方法来设置它们。包含中间件的模块必须实现 NestModule

import { MiddlewareBuilder } from '@nestjs/core';
import { Module, NestModule } from '@nestjs/common';
// 中间件
import { LoggerOneMiddleware } from '../middleware/logger-one.middleware';
export class CatsTwoModule implements NestModule{
configure(consumer:MiddlewareBuilder){
// apply函数中使用逗号分隔我们需要使用的多个中间件
// forRoutes函数中指定的是针对哪个路由。
// forRoutes() 可接受一个字符串、多个字符串、对象、一个控制器类甚至多个控制器类。在大多数情况下,可能只会传递一个由逗号分隔的控制器列表
// 如果我们使用空值,那么这里会将模块中定义的路由全部使用中间件
consumer.apply(LoggerOneMiddleware).forRoutes('')
}
}


* 中间件作用特定的路由

```js
    consumer.apply(LoggerOneMiddleware).forRoutes({ path: 'cats-two/three', method: RequestMethod.POST });
多个特定的值,用逗号分隔
  • exclude 排除不期望的路由,多个也是逗号的形式分割

     consumer.apply(LoggerOneMiddleware)
         .exclude({ path: 'cats-two/three', method: RequestMethod.POST })
         .forRoutes(CatsTwoController);
    
  • 通配符配置

    forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
    
  • 全局使用

    main.ts中使用use方法:
    app.use(LogMiddleware);
    

异常

默认响应

{
  "statusCode": 500,
  "message": "Internal server error"
}

抛出异常

  throw new HttpException('描述的数据', HttpStatus.FORBIDDEN);

HttpException 两个参数

  • response参数定义JSON响应体。可以是astring 或a object,如下所述。
  • status参数定义HTTP状态代码

自定义异常

export class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}

使用
@Get()
async findAll() {
  throw new ForbiddenException();
}

管道

nest g pipe validation pipe
  • 转换:将输入数据转换为所需的形式(例如,从字符串到整数)
  • 验证:评估输入数据,如果有效,只需将其原样传递即可;否则,当数据不正确时抛出异常

内置管道

  • ValidationPipe
  • ParseIntPipe 参数转换为JavaScript整数(如果转换失败,则抛出异常)
  • ParseBoolPipe boolean
  • ParseArrayPipe array
  • ParseUUIDPipe 仅需要特定版本的UUID
  • DefaultValuePipe 设置默认值

它们是从@nestjs/common包装中导出的。

@Injectable()
export class ValidationOnePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}

使用

 // id 
  @Post('three/:id')
  postThree(@Param('id',ParseIntPipe) id:number) {
    console.log(id);
    return 'three';
  }

如果接受的参数是数字则正常返回结果

如果需要编写报错信息


async findOne(
  @Param('id', new ParseIntPipe(
      { errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }
  ))
  id: number,
)

输入的类型
export interface ParseIntPipeOptions {
    errorHttpStatusCode?: ErrorHttpStatusCode;
    // 我感觉是拿到操作的信息进行的某些操作
    exceptionFactory?: (error: string) => any;
}

自定义管道

需要传参数,管道才能接受到值@Query/@Params/@Body

参数

  • value 是当前处理的参数
  • metadata 是其元数据。
export interface ArgumentMetadata {
  type: 'body' | 'query' | 'param' | 'custom';
  metatype?: Type<unknown>;
  data?: string;
}
参数 描述
type 告诉我们该属性是一个 body @Body(),query @Query(),param @Param() 还是自定义参数 在这里阅读更多
metatype 属性的元类型,例如 String。 如果在函数签名中省略类型声明,或者使用原生 JavaScript,则为 undefined
data 传递给装饰器的字符串,例如 @Body('string')。 如果您将括号留空,则为 undefined

yarn add joi -S

yarn add @types/joi -D

joi很强大,我会单独出一篇博客介绍

创建一个过滤器

创建一个过滤器
nest g pipe xxx

import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
import { Schema } from 'joi';

@Injectable()
export class ValidationOnePipe implements PipeTransform {
                            // 要求是joi模式实例
    					// ObjectSchema对象实例
  constructor(private readonly schema: Schema) {
  }

  // value 是传进来的值, 从metadata中可以取出 相关类型 Body Query 等
  transform(value: any, metadata: ArgumentMetadata) {
    const { error } = this.schema.validate(value);
    if (error) {
      throw new BadRequestException(error.message || 'Validation failed');
    }
    return value;
  }
}

校验条件

// cat.ts
const Joi = require('joi');
// 下面这种方式引入会报错的
// import Joi from 'joi'
export const Cat = Joi.object({
  color: Joi.string().required(),
});
// 如果将方法参数的类型声明为接口而不是类,则该metatype值为Object。
export class CreateCatDto {
  color: string;
}

使用

import { Body, Controller, Get, HttpStatus, Param, ParseIntPipe, Post, UsePipes } from '@nestjs/common';
import { ValidationOnePipe } from 'src/validation-one.pipe';
import { Cat, CreateCatDto } from './cat';

@Controller('cats-two')
export class CatsTwoController {
  // 第一种方式
  @Post('cat')
  @UsePipes(new ValidationOnePipe(Cat)) // 此处会直接把body 或者 query 传到transform 里 也就是value
  cat(@Body() body:CreateCatDto) {
    return body;
  }
  //第二种方式
  @Get('cats')
  catAll(@Body(new ValidationOnePipe(Cat)) body:CreateCatDto) {
    return body;
  }
}

类校验

yarn add class-validator class-transformer -S

https://github.com/typestack/class-validator#usage

https://github.com/typestack/class-transformer

类的接口

import { IsInt, IsString } from 'class-validator';

export class CreateCatDet {
  @IsString({message:'报错信息'})
  name: string;
  @IsInt()
  age: number;
  @IsString()
  breed: string;
}
  @Post('three')
		 // 校验器                      // 类的接口
  threeAll(@Body(new CommondityPipe()) body:CreateCatDet){
    return body
  }

类管道

import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
@Injectable()
export class CommondityPipe implements PipeTransform {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    // 转化处理
    value.age = +value.age;
    // plainToClass()将普通的JavaScript参数对象转换为类型对象
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0)
      throw new BadRequestException('报错啦');
    return value;
  }
}

全局管道

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

是不是类似于微服务

也可以把管道放在全局

import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_PIPE,
      useClass: ValidationPipe,
    },
  ],
})
export class AppModule {}

设置默认值

  @Get('four')
  async findAll(
    @Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
    @Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
  ) {
    return { page, activeOnly };
  }