天天看点

nodejs企业级开发框架nest学习总结 - 1.NestJS入门controller、DTO、providers、module

NestJS入门controller、DTO、providers、module。

官方API地址https://docs.nestjs.com/

Nest(或NestJS)是一个用于构建高效,可扩展的Node.js服务器端应用程序的框架。也可以在Nest中使用express框架的扩展

安装(官方也有推荐)

npm i -g @nestjs/cli //全局安装脚手架
 nest new project-name //使用脚手架创建nest项目
           

脚手架创建的项目自带:@nestjs/core @nestjs/common rxjs reflect-metadata这些依赖

项目目录:src下面的主要文件

xxx.controller.ts 是一个nest的路由控制器
xxx.controller.spec.ts 是一个控制器测试文件
xxx.service.ts 对应的服务,把控制器的一些实现过程的抽离,让controller文件不会太过臃肿,也减少了耦合度
xxx.module.ts 是把控制器,服务等注册成一个应用程序的模块文件
main.ts 主要文件,是程序的主入口,也是启动整个项目的主文件,可以和express那样配置中间件等操作,使用核心功能NestFactory创建Nest应用程序实例的应用程序的条目文件。
           

1.控制器 (路由控制器)

用装饰器书写后端路由

@Controller() // 装饰一个类,把类注册成路由控制器,可以传递字符串路由参数或者路由表达式等

例如: @Controller(‘user’) => 每次请求内部路由方法:localhost:3000/user …

Get, Post, Put, Delete, Patch等装饰器修饰类的方法,把方法注册成每一个路由,分别是get、post、put、delete、patch请求等

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller('user') //(注册路由必选装饰器),传入一个可选参数,可以让类内部所有的路由加上这个参数,/user user 看习惯一个样
export class AppController {
 // constructor构造函数创建之后,会在providers中找到对应的被Injectable修饰的类,注入到这里
  constructor(private readonly appService: AppService) { } // Nest是围绕通常称为依赖注入的强大设计模式构建的,类型解析
  @Get() // 把这个类方法注册成一个get路由
  public getHello(): string {
    return 'Hello World';
  }
}
           
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
  controllers: [AppController], // 在此模块中定义的控制器集,必须进行实例化
  providers: [AppService], // 注册服务成一个提供者,提供给controller使用
})
export class AppModule { }
           
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
async function bootstrap() {
  // const app = await NestFactory.create<NestExpressApplication>(AppModule); // 使用express
  // 设置全局前缀
  // app.setGlobalPrefix('/api');
  const app = await NestFactory.create(AppModule); // 默认使用
  await app.listen(3000);
}
bootstrap();
           
设置全局前缀 app.setGlobalPrefix(’/api’); //然后整个API请求都需要加上/api

这时候用get请求访问http://localhost:3000/user就会返回一个字符串 'Hello World'

Get, Post, Put, Delete, Patch等装饰器也可以传递字符串参数或者一些正则表达式

//方式1 匹配/
@Get()
//方式2 匹配/get
@Get('get') // or @Get('/get')
//方式3 匹配/get/字符串id
@Get('get/:id')
//方式4 匹配/user/任意字符
@Get('user/*')
           

Post, Put, Delete, Patch等装饰器也是一样的用法,以及类装饰器Controller也是可以这样写

当getHello方法的逻辑操作过于频繁的情况下,可以抽离出来,写到service服务层上面,让逻辑更加清晰,耦合度降低
//app.service.ts
import { Injectable } from '@nestjs/common';
/**
 *  控制器应处理HTTP请求并将更复杂的任务委派给提供者。提供程序是纯类JavaScript类,在@Injectable()类声明之前有一个装饰器。
 * * Injectable装饰器,控制反转(“IoC”)容器
 */
@Injectable() // 控制反转(“IoC”)容器
export class AppService<T = any> {
  public getHello(): string {
    return 'Hello World!';
  }
}

           

服务层 使用Injectable装饰器装饰,让module模块层的providers能够扫描到这个服务层,用于controller层的注入,使得抽离出的部分写在service层

这时候的app.controller.ts

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller('user') //(注册路由必选装饰器),传入一个可选参数,可以让类内部所有的路由加上这个参数,/user user 看习惯一个样
export class AppController {
 // constructor构造函数创建之后,会在providers中找到对应的被Injectable修饰的类,注入到这里
  constructor(private readonly appService: AppService) { } // Nest是围绕通常称为依赖注入的强大设计模式构建的,类型解析
  @Get() // 把这个类方法注册成一个get路由
  public getHello(): string {
    return this.appService.getHello();
  }
}
           
这样的结果也是一致的.

其它:

当你需要接收前端传递过来的参数,获取请求头、获取session、request、response、next的时候,可以使用形参装饰器

// app.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { AppService } from './app.service';
@Controller('user') //(注册路由必选装饰器),传入一个可选参数,可以让类内部所有的路由加上这个参数,/user user 看习惯一个样
export class AppController {
 // constructor构造函数创建之后,会在providers中找到对应的被Injectable修饰的类,注入到这里
  constructor(private readonly appService: AppService) { } // Nest是围绕通常称为依赖注入的强大设计模式构建的,类型解析
  @Get() // 把这个类方法注册成一个get路由
  public getHello(): string {
    return this.appService.getHello();
  }
  @Post('/body')//获取body里面的name参数并返回
  public getBody(@Body('name') name: string): string {
	return name;
  }
}
           

@Body(‘name’) //获取整个body对象的name属性的值

@Body() //则表示获取整个body对象

Param、Headers、Query、Session等用法一致

还有一些装饰器:

HttpCode:成功返回的status 默认是200

Redirect:请求重定向,让这个请求结果重定向到Redirect的那个uri

Next, Res, Req就是express的三剑客了,相信都知道的了,用这三个装饰变量,即可获得对应的属性

@HttpCode(202) // 让成功的响应从默认值200变成202
  @Get('test')
  public test(@Req() request: Request, @Res() response: Response, @Next() next: (url?: string) => void): string {
    // next('/url');
    // next();
    return 'test';
  }
           

2.DTO 数据传输对象(架构) 用于存放传输的数据,例如获取的数据对象、返回的数据对象

// body.dto.ts
export class CreateBodyDto {
    public readonly username: string;
    public readonly password: string;
}

           
创建一个body dto,用于接收整个body
// app.controller.ts 补充
import { CreateBodyDto } from './DTO/body.dto';
  @Post('/body')
  public validate(@Body() body: CreateBodyDto): CreateBodyDto {
    return body;
  }
           
当然@Body() body: any 也是可以的,但是这样违背了ts的强类型语言的特点,同时也降低了代码的复用性等,让代码的可读性更差,使用DTO类来修饰接收或者返回的参数的情况下,可以有更友好的提示和复用性等。

dto:主要用于数据传输对象,就是接收对象或者返回的对象,都可以,使用也较为简单

3.providers

被Injectable装饰器修饰的类就是一个服务,服务可以0个,也可以多个,然后注册在module的providers中,提供给controller等使用
@Module({
	controllers: [AppController], // 在此模块中定义的控制器集,必须进行实例化,可以多个,一个控制器数组
	providers:[appService,pageService...] // 一个服务的数组
})
export class AppModule { }
           
可选服务
import { Injectable, Optional, Inject } from '@nestjs/common';
/**
 *  控制器应处理HTTP请求并将更复杂的任务委派给提供者。提供程序是纯类JavaScript类,在@Injectable()类声明之前有一个装饰器。
 * * Injectable装饰器,控制反转(“IoC”)容器
 */
@Injectable() // 控制反转(“IoC”)容器
export class AppService<T = any> {
  public getHello(): string {
    return 'Hello World!';
  }
  // Inject 注入:可以基于构造函数的注入,也可以是基于属性的注入
  // Optional 可选的 加上这个注解最后,表示这个参数的注入是可以选的
  @Optional()
  @Inject('HTTP_OPTIONS') // Identification:标识/token:令牌 提供一个唯一的标识,防止重复
  private readonly course: T;
}
           

Optional, Inject 装饰属性,可以告诉nest,这是可选的一个选项,同时也是可以注入的选项,这些注入是nest自动注入的,我们可以在相应的情况下调用这些。

4.module 模块是用@Module()装饰器注释的类。

每个应用程序至少有一个模块,一个根模块。@Module()接收一个对象,参数如下:
  • providers 将由Nest注入器实例化的提供程序,并且至少可以在此模块中共享
  • controllers 在此模块中定义的控制器集,必须进行实例化
  • imports 导出此模块中所需的提供程序的导入模块列表
  • exports 其子集providers由此模块提供,并且应该在导入此模块的其他模块中可用
而我们上面的例子就用到了providers、controllers两个接收数组的属性

当我们需要对模块进行拆分的时候,可以使用imports接收一个模块数组

// child.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
    controllers: [AppController], // 在此模块中定义的控制器集,必须进行实例化
    providers: [AppService],
    exports: [AppService], 
})
// 实现NestModule接口,从而配置中间件
export class ChildModule implements NestModule { }
           

每个模块都自动成为共享模块。一旦创建,它可以被任何模块重用。

我们想要共享AppService服务的实例给其它模块使用,为了做到这一点,我们需要导出providers里面的服务,提供给其他模块使用

改造app.module.ts
import { Module } from '@nestjs/common';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
import { ChildModule } from './child.module';

@Module({
  imports: [ChildModule], // 导出此模块中所需的提供程序的导入模块列表,就是把module模块划分成一个个子模块,然后导入到这个模块当中
  // controllers: [AppController], // 在此模块中定义的控制器集,必须进行实例化
  // providers: [AppService], // 注册服务成一个提供者,提供给controller使用
})
export class AppModule { } 
           

exports 也可以用于模块重新导出,让此模块给其他模块使用,而不是直接给主模块.

模块类也可以注入提供者(例如,用于

配置

目的)

import { Global, Module } from '@nestjs/common';
@Global()
@Module({
  //...
})
export class AppModule { 
	constructor(private readonly catsService: CatsService) {}
} 
           
@Global()装饰模块后这个模块就是全局模块,全局模块只应注册一次,通常由根模块或核心模块注册。

动态模块,此功能使您可以轻松创建可自定义的模块

// database.module.ts
import { Module, DynamicModule } from '@nestjs/common';
@Module({})
export class DatabaseModule {
  static forRoot(entities = [], options?): DynamicModule { // forRoot()方法可以同步或异步(即通过a Promise)返回动态模块。
    return {
      module: DatabaseModule, // 模块就是自己这个类
      providers: entities, //返回的模块
      // exports: entities, //导出的模块 exports的子集就是providers
    };
  }
}
           

forRoot()方法可以同步或异步(即通过a Promise)返回动态模块

动态模块的使用

import { Module } from '@nestjs/common';
import { ChildModule } from './child.module';
import { DatabaseModule } from './database.module';
@Module({
  imports: [DatabaseModule.forRoot([ChildModule])],
  exports: [DatabaseModule],
})
export class AppModule {}
           
觉得不错就收个藏什么的,谢谢

先介绍一下nest的基本控制器使用、DTO、provider、module的使用,下节介绍中间件middleware、异常过滤器exceptionFilter、管道pipe的使用

其它博客:

routing-controllers、class-validator、typedi的使用总结(express使用类似nest控制器的装饰器写法等)

Type-GraphQL结合装饰器写法的node框架的学习笔记

等等等…