NestJS 环境搭建(二)| 青训营笔记

112 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天。我们 Bocchi 小组采用 NestJS 开发这次掘金站点的后端,这几天我们尝试编写了统一返回格式、异常处理模块,写篇文章来记录下实现过程。

仓库地址: github.com/Bocchi-Deve…

统一返回格式

为了前端方便处理数据,后端肯定需要将请求结果进行统一返回,通过协商我们小组定义的接口返回格式如下。

{
  "code": 200,
  "success": true,
  "data": {
    "name": "@bocchi/juejin-core",
    "author": "Bocchi-Developers <https://suemor.com>",
    "version": "0.0.1",
    "homepage": "https://github.com/Bocchi-Developers/juejin-core#readme",
    "issues": "https://github.com/Bocchi-Developers/juejin-core/issues"
  }
}

代码实现如下

/* eslint-disable @typescript-eslint/consistent-type-imports */
import type { Observable } from 'rxjs'
import { map } from 'rxjs/operators'

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import * as SYSTEM from '~/constants/system.constant'
export interface Response<T> {
  data: T
}

@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, Response<T>> {
  constructor(private readonly reflector: Reflector) {}
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<Response<T>> {
    if (!context.switchToHttp().getRequest()) {
      return next.handle()
    }
    const handler = context.getHandler()
    const bypass = this.reflector.get<boolean>(
      SYSTEM.RESPONSE_PASSTHROUGH_METADATA,
      handler,
    )
    if (bypass) {
      return next.handle()
    }

    return next.handle().pipe(
      map((data) => {
        return {
          code: context.switchToHttp().getResponse().statusCode || 200,
          success: true,
          data,
        }
      }),
    )
  }
}

全局异常处理

为了使我们后端能够正常运行,进行全局异常是必须的,为了方便前端处理,我们的格式尽可能与统一返回格式相似,我们小组定义的格式如下。

{
  "code": 403,
  "success": false,
  "message": "密码不正确"
}

代码具体实现如下

import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
} from '@nestjs/common'

type myError = {
  readonly status: number
  readonly statusCode?: number
  readonly message?: string
}

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp()
    const response = ctx.getResponse()

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : (exception as myError)?.status ||
          (exception as myError)?.statusCode ||
          HttpStatus.INTERNAL_SERVER_ERROR

    const res = (exception as any).response
    response
      .status(status)
      .type('application/json')
      .send({
        code: res?.statusCode || 500,
        success: false,
        message: res?.message || (exception as any)?.message || '未知错误',
      })
  }
}

注册

为了能够正常使用到这两个模块,我们需要到 app.module里进行注册,具体代码如下。

@Module({
  imports: [
		....
  ],
  controllers: [AppController],
  providers: [
    {
      provide: APP_FILTER,
      useClass: AllExceptionsFilter,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: ResponseInterceptor,
    },
  ],
})
export class AppModule {}