天天看點

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架構的學習筆記

等等等…