作为一个合格的前端开发,node的知识还是要学学的,一个不小心,找到的工作就要搞全栈开发。其实现在一些需要全栈开发的公司,又是以前端开发的岗位招你进去,那他们一定会在你入职后让你写全栈(已哭),现在公司主流的框架有Egg.js、Express.js、koa.js、nest.js。入职的两家公司分别使用过Eggjs和nestjs,下面我就来讲讲nestjs的一些知识吧。
其实nestjs与springboot思想和接口差不多,但是他的语言又是JavaScript,且看且理解吧。
module
AppModule
是应用程序的根模块,根模块提供了用来启动应用的引导机制,可以包含很多功能模块。使用一个@Module()
装饰器的类,装饰器可以理解成一个封装好的函数,其实是一个语法糖。@Module()
装饰器接收四个属性:providers
、controllers
、imports
、exports
。
1、providers:Nest.js
注入器实例化的提供者,写具体的业务代码,各个模块之间可以共享
2、controllers:处理http请求,包括路由控制,向客户端返回响应,将具体业务逻辑给到providers处理;
3、imports:导入模块的列表,导入其他模块的服务;
4、exports:导出服务的列表,供其他模块导入使用。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Controllers
接收应用程序的特定请求。路由机制控制控制器接收请求。@Controller
装饰器的装饰,该装饰器可以传入一个路径参数,作为访问这个控制器的主路径
// 主路径为app http://localhost:8080/app
@Controller("app")
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
Nest 为所有标准的 HTTP 方法提供了相应的装饰器
@Get 用于查询资源
@Post 用于创建新资源
@Delete 用于删除已有资源
@Put 用于更新已有资源
修饰方法参数的装饰器
@Query @Body @Param @Response
动态路由,最好要放在下面,不然会先匹配,下面路由匹配不成功
通配符
三种正则表达式?(字符0个或1个)、+(字符多于1个)、*(字符0个或无限个)
import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';
@Controller('posts')
export class PostsController {
@Post()
// 获取post请求的数据用@Body()装饰器获取数据
create(@Body() Body:createPostDto){
return{
success:true
}
}
//通配符路径
@Get('id_*')
//此处用@Param('id')获取id的值
detail(@Param('id') id:string){
return{
title:'aaaaa'
}
}
@Put(':id')
//此处用@Param('id')获取id的值,使用createPostDto的规则约束。
update(@Param('id') id:string,@Body() body:createPostDto){
return {
success:true
}
}
//动态路由
@Delete(':id')
remove(@Param('id') id:string){
return{
success:true
}
}
}
Providers
Providers
负责存储数据和检索, 旨在供给Controllers
或者需要此功能的东西使用
provider是用 @Injectable() 装饰器注释的类,这里涉及到nest的设计模式,依赖注入。使用@Injectable
修饰后的 AppService
, 在AppModule
中注册之后,在app.controller.ts
中使用,我们就不需要使用new AppService()
去实例化,直接引入过来就可以用。
//cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';
@Controller('cats')
export class CatsController {
//CatsService 是通过类构造函数注入的.
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
//interfaces/cat.interface.ts
export interface Cat {
name: string;
age: number;
breed: string;
}
// cats.service.ts : 负责数据存储和检索
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
//Nest通过@Injectable()知道CatsService是一个装饰器.
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
Pipes(管道)
管道和拦截器有点像,都是在数据传输过程中的“关卡”,只不过各司其职。
管道有两个类型:
转换:管道将输入数据转换为所需的数据输出;
验证:对输入数据进行验证,如果验证成功继续传递,验证失败则抛出异常;
内置管道:
ValidationPipe:根据class-validator导入配置规则,然后直接使用验证
ParseIntPipe:转换传入的参数为数字
ParseUUIDPipe:验证字符串是否是 UUID(通用唯一识别码)
绑定管道:
使用 DTO 可以清晰的了解对象的结构,使用 Pipes(管道)配合 class-validator
还可以对参数类型进行判断,就不需要写一大堆 if - else验证参数了,还可以在验证失败的时候抛出错误信息。
DTO(数据传输对象)
是一种设计模式之间传输数据的软件应用系统。数据传输目标往往是数据访问对象从数据库中检索数据。数据传输对象与数据交互对象或数据访问对象之间的差异是一个以不具有任何行为除了存储和检索的数据(访问和存取器)。
DTO 本身更像是一个指南
, 在使用API时,方便我们了解请求期望的数据类型
以及返回的数据对象。
我们这里使用class而不是使用interface是因为interface
在编译过程中是被删除的,而且用了Swagger
的装饰器,class
可以给参数加说明。
这里又定义Dto而不使用之前的Entiry是因为单一设计的原则,因为HTTP请求传参和返回的内容可以采用和数据库中保存的内容不同的格式,所以将它们分开可以随着时间的推移及业务变更带来更大的灵活性。
// dto/create-post.dot.ts
export class CreatePostDto {
readonly title: string;
readonly author: string;
readonly content: string;
readonly cover_url: string;
readonly type: number;
}
// posts.controller.ts
...
import { CreatePostDto } from './dto/create-post.dto';
@ApiOperation({ summary: '创建文章' })
@Post()
async create(@Body() post:CreatePostDto) {...}
Guards(守卫)
守卫是一个使用 @Injectable()
装饰器的类,守卫的主要功能是授权,去执行上下文,根据角色数据与路由进行匹配, 决定每个程序允许哪些角色
全局守卫
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard()); // 全局守卫
await app.listen(4000);
}
控制器守卫
守卫加在控制器上面,整个控制器下所有的路由或者说所有的方法都需要进行权限判断
@Controller('role')
@UseGuards(AuthGuard) // 控制器上加守卫
export class RoleController {
@Get()
getList() {
return { code: 200, data: 1 }
}
}
方法守卫
守卫加在控制器的具体方法上,使用这个方法都需要进行权限判断
@Controller('role')
export class RoleController {
@Post()
@UseGuards(AuthGuard) // 方法上加守卫
getInfo() {
return {
data: 11
}
}
}
中间件
中间件是路由处理程序之前调用的函数。作为一个后端服务,设置中间件后,一个请求过来,都会先响应,中间函数结束后才会到具有服务函数。
中间件可以做任何的事情:
1、执行任何代码
2、对请求和响应对象进行更改
3、结束请求 - 响应周期
data-transform-middleware 数据转换中间件
debugTs-middleware 请求时间超时输出日志中间件
filter-query-middleware 过滤参数中间件
jwt-parse-middleware token检测中间件
prometheus-middleware 请求监控中间件
request-log-middleware 请求日志中间件
session-middleware session监控中间件
异常过滤器
内置的异常层负责处理整个应用程序中的所有抛出的异常。当捕获到未处理的异常时,最终用户将收到友好的响应。
基础异常类
HttpException,从 @nestjs/common
包中导入,在发生某些错误情况时发送标准HTTP响应对象。
@Get()
findAll() {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'Forbidden',
}, HttpStatus.FORBIDDEN);
}
// 输出 :
{
"statusCode": 403,
"message": "Forbidden"
}
内置HTTP异常
◼ BadRequestException 请求错误
◼ UnauthorizedException 未授权
◼ NotFoundException 请求路径未找到
◼ ForbiddenException 用户无权限访问该资源,请求失败
◼ RequestTimeoutException 请求超时
◼ UnprocessableException 请求被服务器正确解析,但是包含无效字段
◼ InternalServerErrorException 服务器发生错误
◼ NotImplementedException 服务没有实现请求方式
TypeORM
Nest 与数据库无关, 可以与任何 SQL 或 NoSQL 数据库集成,TypeORM可以把数据库映射成对象, 使用对象封装了数据库操作。
Mongodb
find()查询返回多条数据
findOne()查询返回第一条数据
delete() deleteMany()删除数据
update() updateMany() 更新数据
save() 保存数据
sort()按某字段进行排序,sort('-age')根据年龄字段降序排序
skip()跳过几条数据
limit()限制数据返回数量
运算符
$in 选择字段值等于数组中任何给定值的文档
$and 对一个或多个表达式的数组执行逻辑与运算,第一个为假时不再判断后面的
$not 对指定的表达式执行逻辑“NOT 运算”
$or 运算符对一个或多个表达式的数组执行逻辑“或”运算
//查询所有_id等于1或2的数据
this.mongo.find(CreativeEntity, { where: { _id: {$in: [1,2]}}});
//查询一条_id为1的数据返回ExternalId和_id字段
const model = await this.mongo.findOne<AdxBaseEntity>(EntityMap[dependModel], {
where: {
_id: 1,
},
select: ['ExternalId', '_id'],
});
//更新当_id等于1时数据为set
await this.mongo.update(CampaignEntity, {
_id: 1
}, set);
//新增data数据
const record = new CreativeCategoryPackageEntity();
await this.setData(data, record);
const result = await this.mongo.save(record, UserSession);
// 删除账号
this.mongo.deleteMany(HouseKeeperEntity, { _id: {$in: validIds} }),