完整版本,點擊此處查看 http://blog.poetries.top/2022/05/25/nest-summary
Nest (NestJS) 是一個用于構建高效、可擴展的 Node.js 服務器端應用程序的開發框架。它利用 JavaScript 的漸進增強的能力,使用并完全支持 TypeScript (仍然允許開發者使用純 JavaScript 進行開發),并結合了 OOP (面向對象編程)、FP (函數式編程)和 FRP (函數響應式編程)。
- 在底層,Nest 構建在強大的 HTTP 服務器框架上,例如 Express (默認),并且還可以通過配置從而使用 Fastify !
- Nest 在這些常見的 Node.js 框架 (Express/Fastify) 之上提高了一個抽象級別,但仍然向開發者直接暴露了底層框架的 API。這使得開發者可以自由地使用適用于底層平臺的無數的第三方模塊。
本文基于nest8演示
基礎
創建項目
$ npm i -g @nestjs/cli
nest new project-name
創建一個項目
$ 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
應用程序的根模塊。 -
main.ts
應用程序入口文件。它使用 NestFactory 用來創建 Nest 應用實例。
main.ts
包含一個異步函數,它負責引導我們的應用程序:
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
暴露了一些靜態方法用于創建應用實例 -
create()
方法返回一個實現INestApplication
接口的對象, 并提供一組可用的方法
nest
有兩個支持開箱即用的 HTTP 平臺:express
和fastify
。 您可以選擇最適合您需求的產品
-
platform-express
Express 是一個眾所周知的 node.js 簡約 Web 框架。 這是一個經過實戰考驗,適用于生產的庫,擁有大量社區資源。 默認情況下使用@nestjs/platform-express
包。 許多用戶都可以使用Express
,并且無需采取任何操作即可啟用它。 -
platform-fastify
Fastify
是一個高性能,低開銷的框架,專注于提供最高的效率和速度。
Nest控制器
Nest中的控制器層負責處理傳入的請求, 并返回對客戶端的響應。
[圖片上傳失敗...(image-5b262f-1653558123233)]
控制器的目的是接收應用的特定請求。路由機制控制哪個控制器接收哪些請求。通常,每個控制器有多個路由,不同的路由可以執行不同的操作
通過NestCLi創建控制器:
nest -h
可以看到nest
支持的命令
常用命令:
- 創建控制器:
nest g co user module
- 創建服務:
nest g s user module
- 創建模塊:
nest g mo user module
- 默認以src為根路徑生成
nest g controller posts
表示創建posts的控制器,這個時候會在src目錄下面生成一個posts的文件夾,這個里面就是posts的控制器,代碼如下
import { Controller } from '@nestjs/common';
@Controller('posts')
export class PostsController {
}
創建好控制器后,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配置路由請求數據
Nestjs提供了其他HTTP請求方法的裝飾器
@Get()
@Post()
@Put()
、@Delete()
、@Patch()
、@Options()
、@Head()
和@All()
在Nestjs中獲取Get
傳值或者Post提
交的數據的話我們可以使用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);
}
}
注意
-
關于nest的return
: 當請求處理程序返回 JavaScript 對象或數組時,它將自動序列化為 JSON。但是,當它返回一個字符串時,Nest 將只發送一個字符串而不是序列化它
Nest服務
Nestjs中的服務可以是
service
也可以是provider
。他們都可以通過 constructor 注入依賴關系
。服務本質上就是通過@Injectable()
裝飾器注解的類。在Nestjs中服務相當于MVC
的Model
創建服務
nest g service posts
創建好服務后就可以在服務中定義對應的方法
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('文章標題已存在', HttpStatus.BAD_REQUEST);
}
return {
data: await this.postsRepository.save(post),
message: '創建成功',
};
}
// 分頁查詢列表
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(), // 按條件查詢的數量
total: await this.postsRepository.count(), // 總的數量
pageSize,
pageNum,
};
}
// 根據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()
裝飾器提供了元數據,Nest 用它來組織應用程序結構
[圖片上傳失敗...(image-614ea9-1653558123233)]
每個 Nest 應用程序至少有一個模塊,即根模塊。根模塊是 Nest 開始安排應用程序樹的地方。事實上,根模塊可能是應用程序中唯一的模塊,特別是當應用程序很小時,但是對于大型程序來說這是沒有意義的。在大多數情況下,您將擁有多個模塊,每個模塊都有一組緊密相關的功能。
@module() 裝飾器接受一個描述模塊屬性的對象:
-
providers
由 Nest 注入器實例化的提供者,并且可以至少在整個模塊中共享 -
controllers
必須創建的一組控制器 -
imports
導入模塊的列表,這些模塊導出了此模塊中所需提供者 -
exports
由本模塊提供并應在其他模塊中可用的提供者的子集
// 創建模塊 posts
nest g module posts
Nestjs中的共享模塊
每個模塊都是一個共享模塊。一旦創建就能被任意模塊重復使用。假設我們將在幾個模塊之間共享 PostsService 實例。 我們需要把 PostsService 放到 exports 數組中:
// 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] // 共享模塊導出
})
export class PostsModule {}
可以使用
nest g res posts
一鍵創建以上需要的各個模塊
[圖片上傳失敗...(image-890f8d-1653558123233)]
配置靜態資源
NestJS中配置靜態資源目錄完整代碼
npm i @nestjs/platform-express -S
import { NestExpressApplication } from '@nestjs/platform-express';
// main.ts
async function bootstrap() {
// 創建實例
const app = await NestFactory.create<NestExpressApplication>(AppModule);
//使用方式一
app.useStaticAssets('public') //配置靜態資源目錄
// 使用方式二:配置前綴目錄 設置靜態資源目錄
app.useStaticAssets(join(__dirname, '../public'), {
// 配置虛擬目錄,比如我們想通過 http://localhost:3000/static/1.jpg 來訪問public目錄里面的文件
prefix: '/static/', // 設置虛擬路徑
});
// 啟動端口
const PORT = process.env.PORT || 9000;
await app.listen(PORT, () =>
Logger.log(`服務已經啟動 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();
項目根目錄新建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渲染模板引擎,參數就是文件路徑:default文件夾下的index.ejs
getUser(): any {
return {message: "hello word"} //只有返回參數在模板才能獲取,如果不傳遞參數,必須返回一個空對象
}
}
Cookie的使用
cookie和session的使用依賴于當前使用的平臺,如:express和fastify
兩種的使用方式不同,這里主要記錄基于express平臺的用法
cookie可以用來存儲用戶信息,存儲購物車等信息,在實際項目中用的非常多
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();
接口中設置cookie 使用response
請求該接口,響應一個cookie
@Get()
index(@Response() res){
//設置cookie, signed:true加密
//參數:1:key, 2:value, 3:配置
res.cookie('username', 'poetry', {maxAge: 1000 * 60 * 10, httpOnly: true, signed:true})
//注意:
//使用res后,返回數據必須使用res
//如果是用了render模板渲染,還是使用return
res.send({xxx})
}
cookie相關配置參數
-
domain
String 指定域名下有效 -
expires
Date 過期時間(秒),設置在某個時間點后會在該cookoe
后失效 -
httpOnly
Boolean 默認為false
如果為true
表示不允許客戶端(通過js
來獲取cookie
) -
maxAge
String 最大失效時間(毫秒),設置在多少時間后失效 -
path
String 表示cookie
影響到的路徑,如:path=/
如果路徑不能匹配的時候,瀏覽器則不發送這個cookie
-
secure
Boolean 當secure
值為true
時,cookie
在 HTTP 中是無效,在HTTPS
中才有效 -
signed
Boolean 表示是否簽名cookie
,如果設置為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'));
// 設置cookie的時候配置signed屬性
res.cookie('userinfo','hahaha',{domain:'.ccc.com',maxAge:900000,httpOnly:true,signed:true});
// signedCookies調用設置的cookie
console.log(req.signedCookies);
....
完整版本,點擊此處查看 http://blog.poetries.top/2022/05/25/nest-summary