Node 后台学习笔记-毕设篇(旅游信息管理平台)
-
使用技术栈:Node + ts + nestjs + typeorm + MySQL
-
简介:
- 这是款具有上传下载、发布文章、评论、用户个人中心 的一款 旅游资源管理项目。
- 可对数据库进行操作
-
项目链接: github.com/donwenx
本章学习点:
- 基于错误过滤器实现
- 规范同一错误处理 -
ErrorCode
- 定一个
tag
,为后面的文章进行分类处理
统一错误处理
设置规范的错误码
// /common/error-code.enum.ts
export enum ErrorCode {
UNKNOWN_ERROR = -1, // 未知错误
USER_NOT_LOGGED_IN = 1, // 用户未登录!
DUPLICATE_USER_NAME = 2, // 用户名重复!
PASSWORD_ERROR = 3, // 密码错误!
ACCOUNT_ERROR = 4, // 账号错误!
DATA_DOES_NOT_EXIST = 5, // 数据不存在!
NO_PERMISSION = 6, // 没有权限!
}
// /common/exception.ts
export class AppException {
constructor(public code: number, public message: string) {}
}
使用 exception.filter.ts
用于过滤错误
// /common/exception.filter.ts
import { ArgumentsHost, BadRequestException, Catch, HttpStatus } from "@nestjs/common";
import { AppException } from "./exception";
import { ErrorCode } from "./error-code.enum";
@Catch()
export class ExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse()
const httpResponse = (exception as BadRequestException).getResponse?.() as { message: string[] }
let message = (exception as Error).message
const code = (exception as AppException).code
if (httpResponse) {
message = typeof httpResponse.message == 'string' ? httpResponse.message : httpResponse.message[0]
}
console.log('httpResponse', exception, httpResponse)
response.status(HttpStatus.UNPROCESSABLE_ENTITY).json({
code: code || ErrorCode.UNKNOWN_ERROR,
message: message
})
}
}
在main.ts
中全局挂载
// main.ts
import { ExceptionFilter } from "./common/exception.filter";
app.useGlobalFilters(new ExceptionFilter()); // 挂载错误拦截 全局
如何使用错误规范
- 回到之前写的创建用户接口,
- 其中不是有一个返回用户名重复的错误抛出吗
- 这个时候我们既可以规范这个错误了
AppException
接收两个参数:code: number
,message: string
ErrorCode.DUPLICATE_USER_NAME
是前面规定好的错误码
import { AppException } from "src/common/exception";
import { ErrorCode } from "src/common/error-code.enum";
// 使用方法
if (user !== null) {
// 用户存在抛出错误
// return '用户名错误' 之前是直接return字符串
// 现在我们定义了一个错误码,和message
// AppException接收两个参数:code: number, message: string
throw new AppException(ErrorCode.DUPLICATE_USER_NAME, "用户名重复");
}
测一下,注册重复的用户名是否报错
tags 标签接口实现
- 实现文章相关接口前的准备
- 在实现文章需要给它设置相应的
tags
标签,方便后续分类查找 - 并为了规范,设置了
错误处理
,优化错误不规范
tags 标签实现
和前面写 user 模块一样,先创建出 tags 模块
nest g resource tags
创建实体
tags 实例具备:id、name、userId(用来查询是那个用户创建的)、createTime、updateTime、state(设置删除状态,1 为默认,0 为删除)
// tag.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class Tag {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 255, type: "varchar", unique: true })
name: string;
@Column({ type: "int" })
userId: number;
@Column({ type: "int" })
createTime: number;
@Column({ type: "int" })
updateTime: number;
@Column({ type: "int", default: "1" })
state: number;
}
创建 tagDto
// tags.dto.ts
import { IsString } from "class-validator";
export class CreateTagDto {
@IsString()
name: string;
}
在 tags 模块中导入 tag 实体,并导出 TagsService
// tags.module.ts
import { Module } from "@nestjs/common";
import { TagsService } from "./tags.service";
import { TagsController } from "./tags.controller";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Tag } from "./entities/tag.entity";
@Module({
imports: [TypeOrmModule.forFeature([Tag])], // 注入tag实体
controllers: [TagsController],
providers: [TagsService],
exports: [TagsService], // 导出tagsService
})
export class TagsModule {}
在 app 中注入 tag 实体
// app.module.ts
import { Tag } from './tags/entities/tag.entity';
@Module({
imports: [
TypeOrmModule.forRoot({
// ...
entities: [
// ...
Tag // 注入 tag 实体
],
})
]
})
在 service 层中实现创建功能了
// tags.service.ts
import { Injectable } from '@nestjs/common';
import { Tag } from './entities/tag.entity';
import { getTimeStamp } from 'src/util/function';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class TagsService {
@InjectRepository(Tag)
private repository: Repository<Tag> // 注入tag实体
// 创建tag,并保存到数据库中
async create(createTagDto, userId: number) {
const data = new Tag()
data.name = createTagDto.name
data.userId = userId
data.createTime = getTimeStamp()
data.updateTime = getTimeStamp()
data.state = 1
return await this.repository.save(data)
}
}
实现创建接口
- 传入参数:name,和 token
- 通过 token 获取到 userId
// tags.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { TagsService } from './tags.service';
import { CreateTagDto } from './tags.dto';
import { GetUserId } from 'src/common/user.decorator';
@Controller('tags')
export class TagsController {
constructor(private readonly tagsService: TagsService) { }
@Post()
// GetUserId() 是获取token 中的 用户id
async create(@Body() createTagDto: CreateTagDto, @GetUserId() userId: number) {
return this.tagsService.create(createTagDto, userId)
}
}
查询 tag 接口
查找全部 tags
- API:http://localhost:3000/tags/
- get 请求
- 参数 无
通过 id 查找一个 tag
- API:http://localhost:3000/tags/:id
- get 请求
- 参数(url )
- id:1
先在 service 层中实现查询
// tags.service.ts
async findAll() {
return await this.repository.findBy({ state: 1 })
}
async findOne(id: number) {
return await this.repository.findOneBy({ id: id, state: 1 })
}
调用 service 层的查询,实现接口
// tags.controller.ts
@Get()
// 查询所有的tag
findAll() {
return this.tagsService.findAll()
}
@Get(':id')
// 通过id查找指定的tag
findOne(@Param('id') id: string) {
return this.tagsService.findOne(+id)
}
查找全部 tags
通过 id 查找一个 tag
删除 tag 接口
- API:http://localhost:3000/tags/:id
- delete 请求
- 参数 token、id(tag 的 id)
// tags.service.ts
async remove(id: number) {
const tag = await this.repository.findOneBy({ id: id })
tag.state = 0 // 设置状态为0, 0表示删除
return await this.repository.save(tag)
}
// tags.controller.ts
@Delete(':id')
async remove(@Param('id') id: string, @GetUserId() userId: number) {
const tag = await this.tagsService.findOne(Number(id))
if(tag.userId !== userId) {
throw new AppException(ErrorCode.NO_PERMISSION, '没有删除权限!')
}
return this.tagsService.remove(+id)
}
测试一下非创建 tag 的账户删除,提示错误吗
测试一个可以真的删除吗