nestJS學(xué)習(xí)總結(jié)篇

完整版本,點(diǎn)擊此處查看 http://blog.poetries.top/2022/05/25/nest-summary

Nest (NestJS) 是一個用于構(gòu)建高效、可擴(kuò)展的 Node.js 服務(wù)器端應(yīng)用程序的開發(fā)框架。它利用 JavaScript 的漸進(jìn)增強(qiáng)的能力,使用并完全支持 TypeScript (仍然允許開發(fā)者使用純 JavaScript 進(jìn)行開發(fā)),并結(jié)合了 OOP (面向?qū)ο缶幊蹋?、FP (函數(shù)式編程)和 FRP (函數(shù)響應(yīng)式編程)。

  • 在底層,Nest 構(gòu)建在強(qiáng)大的 HTTP 服務(wù)器框架上,例如 Express (默認(rèn)),并且還可以通過配置從而使用 Fastify !
  • Nest 在這些常見的 Node.js 框架 (Express/Fastify) 之上提高了一個抽象級別,但仍然向開發(fā)者直接暴露了底層框架的 API。這使得開發(fā)者可以自由地使用適用于底層平臺的無數(shù)的第三方模塊。

本文基于nest8演示

基礎(chǔ)

創(chuàng)建項(xiàng)目

$ npm i -g @nestjs/cli

nest new project-name 創(chuàng)建一個項(xiàng)目

$ tree
.
├── README.md
├── nest-cli.json
├── package.json
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json

2 directories, 12 files

以下是這些核心文件的簡要概述

  • app.controller.ts 帶有單個路由的基本控制器示例。
  • app.module.ts 應(yīng)用程序的根模塊。
  • main.ts 應(yīng)用程序入口文件。它使用 NestFactory 用來創(chuàng)建 Nest 應(yīng)用實(shí)例。

main.ts 包含一個異步函數(shù),它負(fù)責(zé)引導(dǎo)我們的應(yīng)用程序:

import { NestFactory } from '@nestjs/core';
import { ApplicationModule } from './app.module';
        
async function bootstrap() {
  const app = await NestFactory.create(ApplicationModule);
  await app.listen(3000);
}
bootstrap();
  • NestFactory 暴露了一些靜態(tài)方法用于創(chuàng)建應(yīng)用實(shí)例
  • create() 方法返回一個實(shí)現(xiàn) INestApplication 接口的對象, 并提供一組可用的方法

nest有兩個支持開箱即用的 HTTP 平臺:expressfastify。 您可以選擇最適合您需求的產(chǎn)品

  • platform-express Express 是一個眾所周知的 node.js 簡約 Web 框架。 這是一個經(jīng)過實(shí)戰(zhàn)考驗(yàn),適用于生產(chǎn)的庫,擁有大量社區(qū)資源。 默認(rèn)情況下使用 @nestjs/platform-express 包。 許多用戶都可以使用 Express ,并且無需采取任何操作即可啟用它。
  • platform-fastify Fastify 是一個高性能,低開銷的框架,專注于提供最高的效率和速度。

Nest控制器

Nest中的控制器層負(fù)責(zé)處理傳入的請求, 并返回對客戶端的響應(yīng)。

[圖片上傳失敗...(image-5b262f-1653558123233)]

控制器的目的是接收應(yīng)用的特定請求。路由機(jī)制控制哪個控制器接收哪些請求。通常,每個控制器有多個路由,不同的路由可以執(zhí)行不同的操作

通過NestCLi創(chuàng)建控制器:

nest -h 可以看到nest支持的命令

常用命令:

  • 創(chuàng)建控制器:nest g co user module
  • 創(chuàng)建服務(wù):nest g s user module
  • 創(chuàng)建模塊:nest g mo user module
  • 默認(rèn)以src為根路徑生成
image.png
nest g controller posts

表示創(chuàng)建posts的控制器,這個時候會在src目錄下面生成一個posts的文件夾,這個里面就是posts的控制器,代碼如下

import { Controller } from '@nestjs/common';

@Controller('posts')
export class PostsController {
}

創(chuàng)建好控制器后,nestjs會自動的在 app.module.ts 中引入PostsController,代碼如下

// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PostsController } from './posts/posts.controller'
    
@Module({
    imports: [],
    controllers: [AppController, PostsController],
    providers: [AppService],
})
export class AppModule {}

nest配置路由請求數(shù)據(jù)

Nestjs提供了其他HTTP請求方法的裝飾器 @Get() @Post() @Put() 、 @Delete()、 @Patch()、 @Options()、 @Head()@All()

在Nestjs中獲取Get傳值或者Post提交的數(shù)據(jù)的話我們可以使用Nestjs中的裝飾器來獲取。

@Request()  req
@Response() 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]

示例

@Controller('posts')
export class PostsController {
  constructor(private readonly postsService: PostsService) {}

  @Post('create')
  create(@Body() createPostDto: CreatePostDto) {
    return this.postsService.create(createPostDto);
  }

  @Get('list')
  findAll(@Query() query) {
    return this.postsService.findAll(query);
  }

  @Get(':id')
  findById(@Param('id') id: string) {
    return this.postsService.findById(id);
  }

  @Put(':id')
  update(
    @Param('id') id: string,
    @Body() updatePostDto: UpdatePostDto,
  ) {
    return this.postsService.update(id, updatePostDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.postsService.remove(id);
  }
}

注意

  • 關(guān)于nest的return: 當(dāng)請求處理程序返回 JavaScript 對象或數(shù)組時,它將自動序列化為 JSON。但是,當(dāng)它返回一個字符串時,Nest 將只發(fā)送一個字符串而不是序列化它

Nest服務(wù)

Nestjs中的服務(wù)可以是service 也可以是provider。他們都可以通過 constructor 注入依賴關(guān)系。服務(wù)本質(zhì)上就是通過@Injectable() 裝飾器注解的類。在Nestjs中服務(wù)相當(dāng)于MVCModel

image.png

創(chuàng)建服務(wù)

nest g service posts

創(chuàng)建好服務(wù)后就可以在服務(wù)中定義對應(yīng)的方法

import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Not, Between, Equal, Like, In } from 'typeorm';
import * as dayjs from 'dayjs';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { PostsEntity } from './entities/post.entity';
import { PostsRo } from './interfaces/posts.interface';

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(PostsEntity)
    private readonly postsRepository: Repository<PostsEntity>,
  ) {}

  async create(post: CreatePostDto) {
    const { title } = post;
    const doc = await this.postsRepository.findOne({ where: { title } });
    console.log('doc', doc);
    if (doc) {
      throw new HttpException('文章標(biāo)題已存在', HttpStatus.BAD_REQUEST);
    }
    return {
      data: await this.postsRepository.save(post),
      message: '創(chuàng)建成功',
    };
  }

  // 分頁查詢列表
  async findAll(query = {} as any) {
    let { pageSize, pageNum, orderBy, sort, ...params } = query;
    orderBy = query.orderBy || 'create_time';
    sort = query.sort || 'DESC';
    pageSize = Number(query.pageSize || 10);
    pageNum = Number(query.pageNum || 1);
    console.log('query', query);
    
    const queryParams = {} as any;
    Object.keys(params).forEach((key) => {
      if (params[key]) {
        queryParams[key] = Like(`%${params[key]}%`); // 所有字段支持模糊查詢、%%之間不能有空格
      }
    });
    const qb = await this.postsRepository.createQueryBuilder('post');

    // qb.where({ status: In([2, 3]) });
    qb.where(queryParams);
    // qb.select(['post.title', 'post.content']); // 查詢部分字段返回
    qb.orderBy(`post.${orderBy}`, sort);
    qb.skip(pageSize * (pageNum - 1));
    qb.take(pageSize);

    return {
      list: await qb.getMany(),
      totalNum: await qb.getCount(), // 按條件查詢的數(shù)量
      total: await this.postsRepository.count(), // 總的數(shù)量
      pageSize,
      pageNum,
    };
  }

  // 根據(jù)ID查詢詳情
  async findById(id: string): Promise<PostsEntity> {
    return await this.postsRepository.findOne({ where: { id } });
  }

  // 更新
  async update(id: string, updatePostDto: UpdatePostDto) {
    const existRecord = await this.postsRepository.findOne({ where: { id } });
    if (!existRecord) {
      throw new HttpException(`id為${id}的文章不存在`, HttpStatus.BAD_REQUEST);
    }
    // updatePostDto覆蓋existRecord 合并,可以更新單個字段
    const updatePost = this.postsRepository.merge(existRecord, {
      ...updatePostDto,
      update_time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
    });
    return {
      data: await this.postsRepository.save(updatePost),
      message: '更新成功',
    };
  }

  // 刪除
  async remove(id: string) {
    const existPost = await this.postsRepository.findOne({ where: { id } });
    if (!existPost) {
      throw new HttpException(`文章ID ${id} 不存在`, HttpStatus.BAD_REQUEST);
    }
    await this.postsRepository.remove(existPost);
    return {
      data: { id },
      message: '刪除成功',
    };
  }
}

Nest模塊

模塊是具有 @Module() 裝飾器的類。 @Module() 裝飾器提供了元數(shù)據(jù),Nest 用它來組織應(yīng)用程序結(jié)構(gòu)

[圖片上傳失敗...(image-614ea9-1653558123233)]

每個 Nest 應(yīng)用程序至少有一個模塊,即根模塊。根模塊是 Nest 開始安排應(yīng)用程序樹的地方。事實(shí)上,根模塊可能是應(yīng)用程序中唯一的模塊,特別是當(dāng)應(yīng)用程序很小時,但是對于大型程序來說這是沒有意義的。在大多數(shù)情況下,您將擁有多個模塊,每個模塊都有一組緊密相關(guān)的功能。

@module() 裝飾器接受一個描述模塊屬性的對象:

  • providers 由 Nest 注入器實(shí)例化的提供者,并且可以至少在整個模塊中共享
  • controllers 必須創(chuàng)建的一組控制器
  • imports 導(dǎo)入模塊的列表,這些模塊導(dǎo)出了此模塊中所需提供者
  • exports 由本模塊提供并應(yīng)在其他模塊中可用的提供者的子集
// 創(chuàng)建模塊 posts
nest g module posts

Nestjs中的共享模塊

每個模塊都是一個共享模塊。一旦創(chuàng)建就能被任意模塊重復(fù)使用。假設(shè)我們將在幾個模塊之間共享 PostsService 實(shí)例。 我們需要把 PostsService 放到 exports 數(shù)組中:

// posts.modules.ts
import { Module } from '@nestjs/common';
import { PostsController } from './posts.controller';
import { PostsService } from './posts.service';
@Module({
  controllers: [PostsController],
  providers: [PostsService],
  exports: [PostsService] // 共享模塊導(dǎo)出
})
export class PostsModule {}

可以使用 nest g res posts 一鍵創(chuàng)建以上需要的各個模塊

[圖片上傳失敗...(image-890f8d-1653558123233)]

配置靜態(tài)資源

NestJS中配置靜態(tài)資源目錄完整代碼

npm i @nestjs/platform-express -S
import { NestExpressApplication } from '@nestjs/platform-express';
// main.ts
async function bootstrap() {
  // 創(chuàng)建實(shí)例
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  
   //使用方式一
  app.useStaticAssets('public')  //配置靜態(tài)資源目錄
  
  // 使用方式二:配置前綴目錄 設(shè)置靜態(tài)資源目錄
  app.useStaticAssets(join(__dirname, '../public'), {
    // 配置虛擬目錄,比如我們想通過 http://localhost:3000/static/1.jpg 來訪問public目錄里面的文件
    prefix: '/static/', // 設(shè)置虛擬路徑
  });
  // 啟動端口
  const PORT = process.env.PORT || 9000;
  await app.listen(PORT, () =>
    Logger.log(`服務(wù)已經(jīng)啟動 http://localhost:${PORT}`),
  );
}
bootstrap();

配置模板引擎

npm i ejs --save

配置模板引擎

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import {join} from 'path';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.setBaseViewsDir(join(__dirname, '..', 'views')) // 放視圖的文件
  app.setViewEngine('ejs'); //模板渲染引擎

  await app.listen(9000);
}
bootstrap();

項(xiàng)目根目錄新建views目錄然后新建根目錄 -> views -> default -> index.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
   <h3>模板引擎</h3>
    <%=message%>
</body>
</html>

渲染頁面

Nestjs中 Render裝飾器可以渲染模板,使用路由匹配渲染引擎

mport { Controller, Get, Render } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  @Get()
  @Render('default/index')  //使用render渲染模板引擎,參數(shù)就是文件路徑:default文件夾下的index.ejs
  getUser(): any {
    return {message: "hello word"}   //只有返回參數(shù)在模板才能獲取,如果不傳遞參數(shù),必須返回一個空對象
  }
}

Cookie的使用

cookie和session的使用依賴于當(dāng)前使用的平臺,如:express和fastify
兩種的使用方式不同,這里主要記錄基于express平臺的用法

cookie可以用來存儲用戶信息,存儲購物車等信息,在實(shí)際項(xiàng)目中用的非常多

npm instlal cookie-parser --save 
npm i -D @types/cookie-parser --save

引入注冊

// main.ts

import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import * as cookieParser from 'cookie-parser'

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  
  //注冊cookie
  app.use(cookieParser('dafgafa'));  //加密密碼
  
  await app.listen(3000);
}
bootstrap();

接口中設(shè)置cookie 使用response

請求該接口,響應(yīng)一個cookie

@Get()
index(@Response() res){
    //設(shè)置cookie, signed:true加密
    //參數(shù):1:key, 2:value, 3:配置
    res.cookie('username', 'poetry', {maxAge: 1000 * 60 * 10, httpOnly: true, signed:true})
    
    //注意:
    //使用res后,返回數(shù)據(jù)必須使用res
    //如果是用了render模板渲染,還是使用return
    res.send({xxx})
}

cookie相關(guān)配置參數(shù)

  • domain String 指定域名下有效
  • expires Date 過期時間(秒),設(shè)置在某個時間點(diǎn)后會在該cookoe后失效
  • httpOnly Boolean 默認(rèn)為false 如果為true表示不允許客戶端(通過js來獲取cookie)
  • maxAge String 最大失效時間(毫秒),設(shè)置在多少時間后失效
  • path String 表示cookie影響到的路徑,如:path=/如果路徑不能匹配的時候,瀏覽器則不發(fā)送這個cookie
  • secure Boolean 當(dāng) secure 值為 true 時,cookie 在 HTTP 中是無效,在 HTTPS 中才有效
  • signed Boolean 表示是否簽名cookie,如果設(shè)置為true的時候表示對這個cookie簽名了,這樣就需要用res.signedCookies()獲取值cookie不是使用res.cookies()

獲取cookie

@Get()
index(@Request() req){
      console.log(req.cookies.username)
      
      //加密的cookie獲取方式
      console.log(req.signedCookies.username)  
      return req.cookies.username
}

Cookie加密

// 配置中間件的時候需要傳參
app.use(cookieParser('123456'));

// 設(shè)置cookie的時候配置signed屬性
res.cookie('userinfo','hahaha',{domain:'.ccc.com',maxAge:900000,httpOnly:true,signed:true});

// signedCookies調(diào)用設(shè)置的cookie
console.log(req.signedCookies);  

....

完整版本,點(diǎn)擊此處查看 http://blog.poetries.top/2022/05/25/nest-summary

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容