Nest-js 随便写写
前言
Nest-js 底层基于 Node.js ,在原由框架的基础上(Express 或者 Fastify),进行的二次框架开发。它是一款高性能的 Web 后端框架,可以很好的结合typeScript
进行后端业务的开发。
本篇文章是作者初学 Nest-js 的一些笔记,目前水平有限,暂时没精力深入研究。
学习这个框架的目的也是为了我的一个社区项目:http://www.kaiven666club.icu/
该项目有个 oj 判题的服务,该服务的核心点在于代码的执行,也就是代码沙箱
的实现。感兴趣的读者可以关注我的gitee
账号,后期会将代码沙箱的代码进行上传的。
Nest-js 的安装与初步启动
全局安装 Nest-js 的脚手架
npm i -g @nestjs/cli
使用脚手架创建一个项目
nest new [项目名称]
查看 package.json 文件
"start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main"
我们可以发现,这么多的启动方式,我们需要热重载,
命令控制台输入
npm run start:dev
命令,启动服务
出现 Nest application successfully started
,表示启动成功
重要文件介绍
dist:编译后的文件目录
node_modules:存放项目所需要的开发类库的
src:工作目录
test:进行单元测试的目录,可以删掉
.eslintrc.js:Eslint的配置文件,可以删掉,很麻烦的
.gitignore:代码文件上传到远程仓库时,需要忽略的文件胚子(如果不需要上传代码的话,可以删掉)
剩下的文件我们不需要关心了,最后要做的就是,卸载掉 Eslint 的相关依赖:
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^8.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0"
这几个,全部给它干掉,执行npm uninstall [依赖名]
最后再次重启项目:npm run start:dev
ok,没有问题。
重要文件介绍
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule); // 创建一个服务器实例
await app.listen(process.env.PORT ?? 3000); // 监听 3000 端口
}
bootstrap();
我们说过,Nest-js 底层有两个框架可以选择,一个是 Express,另一个是 Fastify,这里我们指定 Express:
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); // 创建一个服务器实例
await app.listen(process.env.PORT ?? 3000); // 监听 3000 端口
}
bootstrap();
create<NestExpressApplication>
这个<>
括起来的东西叫做泛型符号,如果你不知道什么是泛型,放我在放屁,学一下typeScript
或者Java
吧。
app.moudle.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
管理模块的容器。
什么是模块?后面你就知道了。
app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
控制器层。
app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
服务提供层。
MVC 架构
这是一种软件设计的范式,具体的东西自己百度。这里简单的说一下请求的流程:
客户端发送请求会打在我们的控制器层,控制器层再调用我们的服务层,当然,如果需要进行数据的持久化的话,还需要调用 dao 层。
简单来说就是,控制器层就是负责请求的接收与最终结果的响应,而服务层提供一些底层的服务。
让我们的数据在浏览器上展示出来
改造
app.controller.ts
@Controller("/app") export class AppController { constructor(private readonly appService: AppService) {} @Get("/test") getHello(): string { return this.appService.getHello(); } }
启动服务,
npm run start:dev
浏览器访问
localhost:3000/app/test
效果图。
Nest-cli 用法
安装 Nest-js 框架时,我们下载了 Nest-cli 脚手架,我们可以使用它的一些指令来简化相关的开发工作:
1、查看所有的脚手架命令
控制台输入nest --help
:
(英文好的你应该能够翻译的吧)
name列:命令参数的全称
alias列:命令参数的别名
descript列:对于命令参数的描述信息
后续我们的命令都是:nest g xxx (表示生成什么东西,一种模板化的生成方式,g 的全称是 generate)
2、生成一套模板
控制台输入
nest g resource user
或者nest g res user
(后者 res 就是前者 resource 的别名)选择第一个
REST API
这是一个前后端接口风格,具体可以百度。
输入
n
,表示不需要CRUD
的模板生成完毕
控制器(Controller)详解
上文说过,控制器的作用就是接收前端的请求,返回给前端对应的结果,现在我们来看一下怎么样盛接前端发送过来的数据:
1、@Request() 获取请求对象
在user.controller.ts
中粘贴上去:
import { Controller, Get, Request } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('/user')
export class UserController {
constructor(private readonly userService: UserService) { }
@Get("/info")
public getUserInfo(@Request() request): object {
console.log(request); // 日志输出request
const userInfo = { // 定义用户信息
name: "kaiven",
age: 18
};
return { // 返回用户信息
data: userInfo,
message: "ok",
code: 200
}
}
}
打开Apipost
(没有的,自己去下载一个),然后请求:
成功返回响应数据,我们会发现,框架自动帮我们做了序列化的动作(将 js 对象转成字节流)
2、@Response() 获取响应对象
import { Controller, Get, Request, Response } from '@nestjs/common';
import { UserService } from './user.service';
import { Response as ResponseType } from 'express';
@Controller('/user')
export class UserController {
private userInfo = {
name: "kaiven",
age: 18
};
constructor(private readonly userService: UserService) {
}
@Get("/info")
public getUserInfo(@Request() request): object {
console.log(request); // 日志输出request
return { // 返回用户信息
data: this.userInfo,
message: "ok",
code: 200
}
}
@Get("/info2")
public getUserInfo2(@Response() response: ResponseType): void {
response.json({
data: this.userInfo,
message: "ok",
code: 200
})
}
}
自己发送请求测试。
3、@Session() 获取保存在服务端的 session 对象
@Get("/info3")
public getUserInfo3(@Session() session:SessionType):object {
console.log(session);
return this.userInfo;
}
interface SessionType {
userInfo:object,
sessionId:string
}
4、@Param(key?: string) 获取请求路径参数
@Get("/info4/:user_id")
public getUserInfo4(@Param("user_id") userId):object{
console.log(typeof userId); // string
return {};
}
5、@Body(key?: string) 获取请求体
@Post("/add")
public addUser(@Body() userInfo:UserInfo):boolean {
const {account,password,nickname} = userInfo;
console.log(account,password,nickname);
return true;
}
interface UserInfo {
account:string,
password:string,
nickname:string
}
6、@Query(key?: string) 获取请求 url 的 query 参数
@Get("/info5")
public getUserInfo5(@Query("test") test:string):object {
console.log(test); // sss
return {};
}
请求地址:localhost:3000/user/info5?test=sss
7、@Headers(name?: string) 获取请求头或者请求头中的某个字段值
@Get("/info6")
public getUserInfo6(@Headers() headers):object {
console.log(headers);
return {};
}
8、@HttpCode 设置响应状态码(协议码)
@Get("info7")
@HttpCode(500)
public getUserInfo7():object {
return {};
}
Session 详解
由于笔者开发的代码沙箱
不是面向用户的,所有不需要保存用户的相关信息,想学习的可以看这篇文章:https://xiaoman.blog.csdn.net/article/details/126327047
Providers (提供者)详解
这里简单说明一下,就好了,具体看这篇文章:https://xiaoman.blog.csdn.net/article/details/126494064
所谓的提供者
就是被 @Injectable() 所标记的类:
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserService {}
然后就可以在module.ts
中被管理了:
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
在providers数组中
出现的类,controllers 中就可以注入providers
数组中出现的依赖了。
(如果你学过 Spring,那么这就是 IOC(控制反转))
Nest 模块详解
一个module.ts
文件就对应一个模块。
模块与模块之间是相互隔离的,如果一个模块想要使用另一个模块中的服务,就需要对服务进行暴露:
user.module.ts:
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
providers: [UserService],
exports:[UserService]
})
export class UserModule {}
user.service.ts:
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserService {
private readonly flag:string = "userService";
public getFlag():string {
return this.flag;
}
}
在其他模块中导入:
import { Module } from '@nestjs/common';
import { SubjectService } from './subject.service';
import { SubjectController } from './subject.controller';
import { UserModule } from 'src/user/user.module';
@Module({
imports:[UserModule], // 导入UserModule模块
controllers: [SubjectController],
providers: [SubjectService],
})
export class SubjectModule {}
subject.controller.ts 中使用:
import { Controller, Get } from '@nestjs/common';
import { SubjectService } from './subject.service';
import { UserService } from 'src/user/user.service';
import { log } from 'console';
@Controller('/subject')
export class SubjectController {
constructor(
private readonly subjectService: SubjectService,
private readonly userService: UserService
) {}
@Get("/test")
public test():object {
log(this.userService.getFlag()); // userService
return {};
}
}
当然,如果是在app.module.ts
中导入,那么默认所有注册的类中都可以注入对应的依赖。
Nest 中间件详解
本质上和 Express 的中间件一样的,底层依赖于它嘛,我这里画了一张图:
客户端发送过来的请求,需要经过层层中间件才能到达我们的 Controller。
我们可以将中间件注册为局部中间件和全局中间件。
局部中间件服务于一个 Controller 甚至是一个路由处理函数;全局中间件则是服务于全部的 Controller。
具体参见这篇文章:https://xiaoman.blog.csdn.net/article/details/126753289
Nest 文件上传
就是文件上传啊,还能怎么的。具体参见:https://xiaoman.blog.csdn.net/article/details/126796646
Nest 响应拦截器
AOP(面向切面编程)知道不,就是它。具体参见:https://xiaoman.blog.csdn.net/article/details/126916268
Nest 异常拦截器
就是 Spring 中的 @ExceptionHandler
,具体使用参见:https://xiaoman.blog.csdn.net/article/details/126943648
Nest 管道转换 和 Nest 管道验证 DTO
怎么说呢,我觉得是框架本身的缺陷吧,有点鸡肋,当然,这东西嘛,见仁见智,我更倾向于在业务层做手动编码判断。
详情参见:https://xiaoman.blog.csdn.net/article/details/127024014 和 https://xiaoman.blog.csdn.net/article/details/127024787
Nest 守卫
这个还是有点意思的,本质上就是一个中间件,只是它是最靠近 Controller 的那一个中间件,但是它任何的拦截器或者管道之前执行。
它的最重要的作用就是去验证客户端有误权限去访问某个 url 路由,它的权限控制就类似于sa-token
了。
详情参见:https://xiaoman.blog.csdn.net/article/details/127175529
Nest 自定义装饰器
这个装饰器虽然可以理解为 java 中的注解,但是还是很不一样的。详情参见:https://xiaoman.blog.csdn.net/article/details/127179126
Nest 整合 swagger
这个可是个好东西哇,大大的方便,详情参见:https://xiaoman.blog.csdn.net/article/details/127181578
总结
撰写这篇文章的同时,笔者也复习了一遍 Nest-js 的常规操作,我也只是会用,止于底层的原理,那就不知道了,感兴趣的小伙伴可以去看官方文档或者相关的教学视频。目前我掌握的这些东西,足够我的项目开发了。
2025/01/08
writeBy kaiven