NestJS 响应处理

如何返回异常

  NestJS中,若需要返回异常客户端,可在 Controller Service 中实现。

  例如,现在需要提示客户端,HTTP CODE 400,并返回错误消息,以Controller为例,实现如下:

import { BadRequestException, Controller, Get } from '@nestjs/common';

@Controller()
export class AppController {

  @Get()
  getHello() {
    throw new BadRequestException('这是一个错误的请求(客户端异常)');
  }
}

  客户端拿到的返回结果: 

{
  "message": "这是一个错误的请求(客户端异常)",
  "error": "Bad Request",
  "statusCode": 400
}

  除BadRequestException之外,NestJS也提供了很多异常响应类,可从@nestjs/common导出。

  下表罗列了NestJS提供的常用异常响应类,供大家参考:

方法名 状态码 解释
BadRequestException 400 错误的请求(客户端的错误)
UnauthorizedException 401 没有权限
NotFoundException 404 没有找到资源
ForbiddenException 403 禁止访问
NotAcceptableException 406 无法解析
RequestTimeoutException 408 请求超时
ConflictException 409 请求与服务器端目标资源的当前状态相冲突
GoneException 410 该资源已经不再可用(永久)
HttpVersionNotSupportedException 505 不支持该http协议
PayloadTooLargeException 413 实体数据过大
UnsupportedMediaTypeException 415 格式不被支持
UnprocessableEntityException 422 语义错误,无法响应
InternalServerErrorException 500 服务器遇到意外的状况,无法完成对请求的处理
NotImplementedException 501 服务器不支持当前请求所需要的某个功能
ImATeapotException 418 彩蛋,也用于一些爬虫警告
MethodNotAllowedException 405 请求方式错误
BadGatewayException 502 网关错误
ServiceUnavailableException 503 而无法处理请求,能会在一些延迟后得到缓解
GatewayTimeoutException 504 网关超时
PreconditionFailedException 412 未满足前提条件

自定义异常类

  除了内置方法,NestJS还提供了自定义异常响应类HttpException,可从@nestjs/common导入。

export declare class HttpException extends IntrinsicException {
    /**
     * @param response string, object describing the error condition or the error cause.
     * @param status HTTP response status code.
     * @param options An object used to add an error cause.
     */
    constructor(response: string | Record<string, any>, status: number, options?: HttpExceptionOptions | undefined);
}

  从截取的HttpException官方interface定义中,可以看到最常用的两个参数:responsestatus

  例如,我现在需要返回给一个内部约定状态码 810提示客户端,服务器仍在处理中

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

@Controller()
export class AppController {

  @Get()
  getHello(): string {
    throw new HttpException('服务器仍在处理中,请稍候重试', 810);
  }
}

  客户端拿到的返回结果: 

{
  "statusCode": 810,
  "message": "服务器仍在处理中,请稍候重试"
}

  假设该810响应类型,使用频率较高,可进一步使用HttpException方法创建自定义异常类,方便统一维护使用。例:

  创建 ProcessingException自定义响应异常类,实现方式如下:

import { HttpException } from '@nestjs/common';

export class ProcessingException extends HttpException {
  public constructor(message: string = '服务器仍在处理中,请稍候重试') {
    super(message, 810); // 810 是内部业务约定状态码
  }
}

  使用 ProcessingException自定义异常类,与官方内置的异常类使用方法一致:

import {  Controller, Get } from '@nestjs/common';
import { ProcessingException } from './processing.exception';

@Controller()
export class AppController {

  @Get()
  getHello() {
    throw new ProcessingException();
  }
}

  由此得出,HttpException可实现自定义响应内容,也可以进一步封装成自定义异常类,统一维护,随处调用,方便维护。

包装响应体

  业务开发中,常常会对客户端响应体进行包装,格式大致如下:

{
  "code": 200,
  "content": "Hello World!",
  "message": "success",
  "timestamp": 1738809371994
}

 若实现该响应体格式,需要对正常响应异常响应,进行拦截包装。

 1. 🚓 正常响应

// src/interceptor/http-success/http-success.interceptor.ts

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

export interface TypeHttpResponseContext<T> {
  content: T;
}

@Injectable()
export class HttpSuccessInterceptor<T>
  implements NestInterceptor<T, TypeHttpResponseContext<T>>
{
  intercept(
    context: ExecutionContext,
    next: CallHandler<T>,
  ): Observable<TypeHttpResponseContext<T>> {
    const response = context.switchToHttp().getResponse();
    return next.handle().pipe(
      map((content) => {
        response.status(HttpStatus.OK);
        return {
          content,
          message: 'success',
          code: HttpStatus.OK,
          timestamp: new Date().valueOf(),
        };
      }),
    );
  }
}

 2. 👮 异常响应

// src/filter/http-exception/http-exception.filter.ts

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

interface TypeResponseData {
  statusCode: number;
  message: unknown;
  error: number;
}

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  public catch(exception: HttpException, host: ArgumentsHost): void {
    const status = exception.getResponse() as TypeResponseData;
    const message =
      typeof status === 'string' ? status : String(status.message);
    host.switchToHttp().getResponse().status(HttpStatus.OK).send({
      code: exception.getStatus(),
      message,
      timestamp: new Date().valueOf(),
    });
  }
}

 3. 🚀 注册使用

 在NestJS中,注册使用对应的拦截、过滤器,有两种方式,一种在AppModule进行注册。另一种是在main.ts中直接注册。可根据实际需求自行选择。更推荐在main.ts中实现,更加简洁。

  在main.ts中注册使用:

import { AppModule } from './app.module';
import { NestFactory } from '@nestjs/core';
import { HttpExceptionFilter } from './filter/http-exception/http-exception.filter';
import { HttpSuccessInterceptor } from './interceptor/http-success/http-success.interceptor';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  app.useGlobalInterceptors(new HttpSuccessInterceptor());
  await app.listen(process.env.PORT ?? 3000);
}

bootstrap();

  在AppModule中注册使用:

import { Module } from '@nestjs/common';
import { AppService } from './app.service';
import { AppController } from './app.controller';
import { HttpExceptionFilter } from './filter/http-exception/http-exception.filter';
import { HttpSuccessInterceptor } from './interceptor/http-success/http-success.interceptor';
import { APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: HttpSuccessInterceptor,
    },
    AppService,
  ],
})
export class AppModule {}

  以上就是该文全部内容,主要参考了NestJS官方文档与自身开发实践,希望能够帮助到大家。

👉 官方文档