Node后台学习笔记-毕设篇-第六章 标签模块和错误处理

43 阅读4分钟

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()); // 挂载错误拦截 全局

06.png

如何使用错误规范

  • 回到之前写的创建用户接口,
  • 其中不是有一个返回用户名重复的错误抛出吗
  • 这个时候我们既可以规范这个错误了
  • 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, "用户名重复");
}

测一下,注册重复的用户名是否报错

07.png

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

通过 id 查找一个 tag

先在 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

03.png

通过 id 查找一个 tag

04.png

删除 tag 接口

// 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 的账户删除,提示错误吗

08.png

测试一个可以真的删除吗

09.png

10.png

目录