四、统一异常处理及统一的请求响应(nestjs+next.js从零开始一步一步创建通用后台管理系统)

191 阅读2分钟

1、统一异常处理

1.1、使用nest内置HttpException

nestjs内置了HttpException以及HttpStatus状态码,使用方法如下:

import { HttpException, HttpStatus, Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(params): string {
    if(!params.id){
      throw new HttpException('id is required', HttpStatus.BAD_REQUEST)
    }
    return 'Hello World!';
  }
}

运行程序并在浏览器中输入http://localhost:8000,系统显示如下:

2025-05-14-21-23-46-image.png

修改正确的参数则显示如下:

2025-05-14-21-24-58-image.png

nest系统中还定义了一些其他常见的 HTTP 异常,如:

  • BadRequestException
  • UnauthorizedException
  • NotFoundException
  • ForbiddenException
  • NotAcceptableException
  • RequestTimeoutException
  • ConflictException
  • GoneException
  • HttpVersionNotSupportedException
  • PayloadTooLargeException
  • UnsupportedMediaTypeException
  • UnprocessableEntityException
  • InternalServerErrorException
  • NotImplementedException
  • ImATeapotException
  • MethodNotAllowedException
  • BadGatewayException
  • ServiceUnavailableException
  • GatewayTimeoutException
  • `PreconditionFailedException

1.2、自定义异常

管理框架的一些通用设置项一般放在common目录下,所以在src下新建common/exceptions/forbidden.exception.ts 文件,内容如下:

import { HttpException, HttpStatus } from "@nestjs/common";

//自定义异常继承HttpException
export class ForbiddenException extends HttpException {
  constructor() {
    //构造函数中调用父方法,并传入错误消息和状态码
    super('自定义错误',HttpStatus.FORBIDDEN);
  }
}  

修改app.service代码,测试自定义异常:

2025-05-14-21-36-25-image.png

1.3、统一异常返回内容

一般使用过滤器在返回异常前对异常信息进行一些处理,返回格式一致的信息,方便前端调用。

在common/filters目录下新建http-exception.filter.ts文件,代码如下:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, Injectable } from '@nestjs/common';
import { Request, Response } from 'express';

//注解捕获http异常
@Catch(HttpException)
//继承自ExceptionFilter过滤器
export class HttpExceptionFilter implements ExceptionFilter {
  //实现catch方法
  catch(exception: HttpException, host: ArgumentsHost) {
    //获取Http上下文
    const ctx = host.switchToHttp();
    //获取响应对象
    const response = ctx.getResponse<Response>();
    //const request = ctx.getRequest<Request>();
    //获取状态码
    const status = exception.getStatus();
    //返回错误信息
    response
      .status(status)
      .json({
        code: status,        
        message: exception.message,
      });
  }
}

HttpExceptionFilter 类 :使用 @Catch(HttpException) 装饰器捕获所有的 HttpException 异常。在 catch 方法中,获取请求和响应上下文,然后根据异常的状态码和消息,返回统一格式的 JSON 响应。

在main.ts中增加全局过滤器:

//注册全局异常过滤器,返回统一的格式
  app.useGlobalFilters(new HttpExceptionFilter());

通过 app.useGlobalFilters(new HttpExceptionFilter()) 全局注册异常过滤器,这样所有的控制器都会使用这个过滤器来处理异常。

测试:

2025-05-14-21-47-03-image.png

2、统一的请求响应成功拦截器

我们还需要一个返回格式的拦截器对请求成功(状态码为 2xx)的数据进行一个格式化,比如返回这样的格式:{code:200,data:T,message:string}

2.1、生成拦截器

nest g interceptor common/interceptors/transform --no-spec

代码:

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";

export interface Response<T> {
  data: T;
}

@Injectable()
export class TransformInterceptor<T>
  implements NestInterceptor<T, Response<T>>
{
  intercept(
    context: ExecutionContext,
    next: CallHandler
  ): Observable<Response<T>> {
    return next
      .handle()
      .pipe(map((data) => ({ code: 200, data, describe: "请求成功" })));
  }
}

2.2、同样的在 main.ts 中进行全局注册

 //注册全局拦截器,返回统一的格式  配置到module中了
  app.useGlobalInterceptors(new TransformInterceptor());

2.3、测试

2025-05-14-21-54-34-image.png

2.4、拦截器和过滤器也可以注册到app.module中,把main.ts中的注册代码删掉,在app.module中增加如下代码:

2025-05-14-21-56-52-image.png