他真的试图教会我用Nest.js写接口耶
前言:学习一下用Nest.js写接口
一.项目初始化
1.安装脚手架
npm i -g @nestjs/cli
2.创建项目
nest new 项目名称
3. cd 到项目
4.安装依赖
npm i
5.启动项目,监视文件,自动重新编译和重新加载服务器
npm run start:dev
6.查看启动项目启动端口号,进入程序入口文件 src/main.ts 查看
浏览器打开 http://localhost:3000/
二.核心文件介绍
app.controller.spec.ts
控制器的单元测试,也不知道怎么用的。
app.controller.ts
路由的基本控制器,就是操作逻辑的地方,处理请求响应。
app.module.ts
应用程序的根模块。
app.service.ts
写数据库查询语句的地方。
main.ts
应用程序的入口文件,它使用核心函数NestFactory
创建一个 Nest 应用程序实例。
三.创建一个接口模块,处理请求
为了快速创建内置验证的 CRUD 控制器,您可以使用 CLI 的CRUD 生成器:nest g resource [name]
。
这里根据官方文档提示创建cats模块
nest g resource cats
选择 REST API风格。
好了,现在src文件目录下多了cats目录,赶紧看看吧。
诶,我们是创建了cats模块了,我们有使用吗,有的,在哪里使用了呢?在应用程序的根模块里
使用了啊,哦,原来使用nest g resource cats
命令创建时,同时使用了,真方便,ok快去看看吧。
四.看看cats模块有啥吧
1.先看controller吧,请求方法都在这了
首先,映入眼帘的是头部引入了很多很多......啥,不知道是啥。
接下去看吧,有一个@Controller('cats')
,这是个装饰器,里面有个参数喔,估计是路由前缀吧,接着看有几个@开头的 post、get、delete的东东,应该是接口方法,嗯,是的,我们去用APIFox请求一下
我这个get请求返回了“This action returns all cats”,这不是我写的啊,在哪里的,走,去看看。
@Get()
findAll() {
return this.catsService.findAll();
}
大哥,我在controller找到这么个玩意,是个service方法耶,让我按着我的Ctrl键点击findAll进去看看先。进到了cats.service.ts
文件,里面有一个findAll
方法,返回了一串英文,我不懂英文喔,改改才行,我是个大帅b!!!
2.你这地址有点单调啊,能不能接收query参数的?
肯定能啊
那行,我给你传个?sex=1,你返回个女
字给我。
这还不简单?
先在controller头顶哪里的@nestjs/common
导入 Req
然后使用
// 导入类型
import { Request } from 'express';
@Get()
findAll(@Req() req: Request): string {
console.log('req:', req.query);
return this.catsService.findAll(req.query.sex as string);
}
ps: 这里获取参数其实可以用 @Query,不知道咋的用了上面这种,哈哈哈
这时候,我打印出了所有的query参数。就差给他返回女
字了。
cats.service.ts
findAll(sex: string) {
const sexObj = {
'0': '男',
'1': '女',
};
return sexObj[sex];
}
写好了,赶紧在APIFox看看结果。
返回结果正确,我还想在post请求传param参数给你,传传传,传你个头头,好吧,那我想查数据库,总行了吧。
五.连接mysql数据库
先安装管理数据库typeorm
和连接数据库mysql2
库
npm install --save @nestjs/typeorm typeorm mysql2
然后在app.module.ts
添加配置
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: '127.0.0.1',
port: 3306,
username: 'root',
password: 'root',
database: 'blog',
entities: ['dist/**/*.entity{.ts,.js}'],
synchronize: true,
}),
CatsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
好了,数据库连接成功。
我们现在是用typeorm
来管理数据库,这个库是通过实体来映射到数据库表的,建表我们以前是直接在数据库中创建表结构,现在我们通过实体来创建库。什么意识呢?接下来一起尝试。
在cats文件夹下,有一个entities
文件夹,用来放实体的,点开,发现有个ts文件,没错我们就是在这个ts文件来写实体,也就是数据库表结构。
我写了这些东西:
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
// 建立一个实体映射到数据库表
@Entity('cats')
export class Cat {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 10 })
name: string;
}
@Entity('cats')用来修饰说,我这个类是一个实体啊,名字叫cats
,其中有字段:id、name,你按照这个结构,在mysql中创建一个表吧。我们打开数据库,看下是不是创建了一个叫cats的表呢。
好,没什么问题,可以映射。我们再添加一个
desc
字段试试
没毛病。
六.操作数据库
实体有了,怎么使用呢? 我们操作数据库正常情况下都是在service中,那么就是去service中使用即可,直接贴代码,我们向数据库中插入一条数据
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { Cat } from './entities/cat.entity';
@Injectable()
export class CatsService {
constructor(
@InjectRepository(Cat)
private readonly catRepository: Repository<Cat>,
) {}
async create(createCatDto: Partial<CreateCatDto>): Promise<Cat> {
console.log(createCatDto);
return this.catRepository.save(createCatDto);
}
}
啊哈,保存的时候报错了,说什么实体没有引用,那么我们就引用它,在cats.module.ts
import { Module } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CatsController } from './cats.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Cat } from './entities/cat.entity';
@Module({
imports: [TypeOrmModule.forFeature([Cat])],
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
service写好了,但是还没调用它呢,去哪里调用啊,controller啊,这么快就忘记了吗。
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Req,
} from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Post('/create')
create(@Body() createCatDto: CreateCatDto) {
console.log(createCatDto);
return this.catsService.create(createCatDto);
}
}
细心的盆友发现了,说你这CreateCatDto
哪里来的?你看看cats目录下有一个dto文件夹,有个create-cat.dto.ts
,它就是从这里来的,哈哈哈
export class CreateCatDto {
name: string;
desc: string;
}
好啦好啦,去ApiFox看下结果
发送请求,OK,没报错,我们去数据库瞧瞧,有没有数据
牛逼啊,大哥,原来写接口这么简单啊!!! 感谢观看!!!
七.中间件你应该了解过吧,在这里应该怎么用呢
老规矩先创建对应的文件,nestjs有快捷创建中间件的命令呢
nest g middleware middleware/reqMi
我创建了一个middleware目录下的叫reqMi
中间件,命名吗,简单了点。
好,看看中间件现在有什么
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class ReqMiMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('你好,我是中间件');
next();
}
}
我这里打印了一句话。
那么怎么调用呢?调用中间件有很多种方式,可以参考官方文档哦。
我现在是在app.module.ts
调用
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(ReqMiMiddleware).forRoutes('cats');
}
}
ReqMiMiddleware
被调用啦,forRoutes('cats'),表示为路由为cats前缀的路由添加这个中间件,nestjs中间件,都是通过匹配路由路径来调用的,官方有比较详细的介绍。
我们现在去看看,是不是调用成功了呀。
控制台打印
ok,中间件应用成功啦。
八.当我们的程序出现异常的时候,该怎么办呢?
那么就轮到异常过滤器(Exception filters)出场了。
Nest附带了一个内置异常层
,负责处理整个应用程序中所有未处理的异常。当您的应用程序代码不处理异常时,它会被此层捕获,然后该层会自动发送适当的响应给客户端。
我们现在抛出一个异常看看,会发生什么
import { HttpException, HttpStatus } from '@nestjs/common';
create(@Body() createCatDto: CreateCatDto) {
console.log(createCatDto);
// 抛出 http异常
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
return this.catsService.create(createCatDto);
}
客户端请求,返回
{
"statusCode": 403,
"message": "Forbidden"
}
接下来我们创建一个通用的异常过滤器
nest g filter filter/http-exception
那么现在src文件夹下,就多了一个filter文件夹,里面有一个http-exception.filter.ts
文件,就是接下来要操作的文件了。
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
// 响应给客户端的结构,你也可以自定义结构,像这种 { code: 1, msg: "" }
// 我通常用0和1来判断请求是否正确,可以判断status是否等于200 来返回status = 1或0
response.status(status).json({
status: status,
msg: '',
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
官方文档给出了上面这个示例,好了,异常过滤器有了,怎么用呢?
1.给某个路由使用
cats.controller.ts
@Get()
@UseFilters(new HttpExceptionFilter())
findAll() {
throw new UnauthorizedException();
return this.catsService.findAll();
}
2.为整个controller使用
@Controller('cats')
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
3.全局范围内使用
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './filter/http-exception.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局异常过滤器
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
好了,异常过滤器,我们会用啦,就是有异常(比如:客户端参数传错了,空参数,类型错误...)我们就用 throw
抛出一个响应值给客户端。细心的盆友发现了,说,你这个throw
后面跟着的是啥啊?
这个其实是 Nest 提供的标准异常继承自HttpException
。这些是从@nestjs/common
包中公开的,代表了许多最常见的 HTTP 异常:401,403...这些,官方文档有提到,点这里查看去到文档,向下滚动就能看到了。
九.管道(参数验证)
快捷创建管道文件
nest g pipe validation pipe
安装相关包
npm i --save class-validator class-transformer
使用
create-cat.dto.ts
import { IsString } from 'class-validator';
export class CreateCatDto {
@IsString()
name: string;
@IsString()
desc: string;
}
validation.pipe.ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
BadRequestException,
} from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } from 'class-transformer';
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, { metatype }: ArgumentMetadata) {
console.log('----');
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToInstance(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
全局使用
main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
十.拦截器 Interceptors
拦截器具有一组有用的功能,这些功能的灵感来自面向方面的编程(AOP)技术。它们使以下几点成为可能:
- 在方法执行之前/之后绑定额外的逻辑
- 转换从函数返回的结果
- 转换从函数引发的异常
- 扩展基本函数行为
- 根据特定条件完全覆盖函数(例如,用于缓存目的)
快捷创建拦截器,创建一个名为transform
的拦截器
nest g interceptor interceptor/transform
十一.Websocket
要开始构建基于 WebSockets 的应用程序,首先安装所需的包:
$ npm i --save @nestjs/websockets @nestjs/platform-socket.io
$ npm i --save-dev @types/socket.io
或者
$ npm i --save @nestjs/websockets @nestjs/platform-socket.io
执行命令创建相应文件
nest g gateway gateway/chat
十二.表关联查询
比如我有一个category表和emoticon表
import { Emoticon } from 'src/emoticon/entities/emoticon.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('category')
export class Category {
@PrimaryGeneratedColumn()
id: number;
// 类目名称
@Column({ length: 5 })
name: string;
@OneToMany(() => Emoticon, (emoticon) => emoticon.category)
emoticon: Emoticon[];
}
import { Category } from 'src/category/entities/category.entity';
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
@Entity('emoticon')
export class Emoticon {
@PrimaryGeneratedColumn()
id: number;
// 图片名称
@Column({ length: 10 })
name: string;
// 图片地址
@Column()
imgUrl: string;
@ManyToOne(() => Category, (category) => category.emoticon)
category: Category;
}
查询分类
findAll() {
return this.categoryRepository.find({ relations: ['emoticon'] });
}
结果
[
{
"id": 2,
"name": "眼睛",
"emoticon": [
{
"id": 7,
"name": "测试",
"imgUrl": "https://gz.bcebos.com/te/hello-1670471717579.jpeg"
}
]
},
]
转载自:https://juejin.cn/post/7149911017068167199