前言
在我们实际的业务开发中,我们可以看到后端接口返回格式都有一定的要求,假如我们统一规定接口的统一返回格式为:
{
data: any; // 业务数据
code: number; // 状态码
msg: string; // 响应信息
timestamp: number; // 时间戳
}
那么在 Nest.js 中,我们应该如何处理呢?
定义响应状态码枚举和类型
- 在
src
目录中新建/enums/index.ts
文件:
/**
* @description: 响应码
*/
export enum RESPONSE_CODE {
NOSUCCESS = -1, // 表示请求成功,但操作未成功
SUCCESS = 200, // 请求成功
BAD_REQUEST = 400, // 请求错误
UNAUTHORIZED = 401, // 未授权
FORBIDDEN = 403, // 禁止访问
NOT_FOUND = 404, // 资源未找到
INTERNAL_SERVER_ERROR = 500, // 服务器错误
}
/**
* @description: 请求提示语
*/
export enum RESPONSE_MSG {
SUCCESS = '请求成功',
FAILURE = '请求失败',
}
- 在
src
目录中新建/typings/index.d.ts
文件:
declare namespace Api {
namespace Common {
/**
* @description: 全局响应体
*/
type Response<T = any> = {
code: number; // 状态码
data?: T; // 业务数据
msg: string; // 响应信息
timestamp: number; // 时间戳
};
/**
* @description: 分页数据
*/
type PageResponse<T = any> = {
current?: number; // 页码
size?: number; // 当前页条数
total?: number; // 总条数
records: T[]; // 业务数据
};
}
}
- 我们可以编写一个公共方法,专门处理接口的返回结果:
import dayjs from 'dayjs';
import { RESPONSE_CODE, RESPONSE_MSG } from '@/enums';
import type { Response } from '@/types';
/**
* @description: 统一返回体
*/
export const responseMessage = <T = any>(
data,
msg: string = RESPONSE_MSG.SUCCESS,
code: number = RESPONSE_CODE.SUCCESS,
): Response<T> => ({ data, msg, code, timestamp: dayjs().valueOf() });
这里大家可以根据自己的实际业务需求修改。
定义响应体 DTO
首先,定义一个统一的响应数据传输对象(DTO),这将作为所有 API
响应的基本结构。
在 src
目录中新建 /dto/response.dto.ts
文件:
import { ApiProperty } from '@nestjs/swagger';
import { RESPONSE_CODE, RESPONSE_MSG } from '@/enums';
export class ResponseDto {
@ApiProperty({
type: Number,
description: '业务状态码',
default: RESPONSE_CODE.SUCCESS,
})
code: number;
@ApiProperty({
type: String,
description: '业务信息',
default: RESPONSE_MSG.SUCCESS,
})
msg: string;
@ApiProperty({ description: '业务数据' })
data?: any;
@ApiProperty({ type: Number, description: '时间戳', default: 1720685424078 })
timestamp: number;
}
HttpException 异常过滤器
创建一个异常过滤器,它负责捕获作为 HttpException
类实例的异常,并为它们设置自定义响应逻辑。
在 src
目录中新建 /filter/http-exception.filter.ts
文件:
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
import { Response } from 'express';
import { responseMessage } from '@/utils';
// @Catch() 装饰器绑定所需的元数据到异常过滤器上。它告诉 Nest这个特定的过滤器正在寻找
@Catch(HttpException)
export class HttpExceptionsFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
// 获取上下文
const ctx = host.switchToHttp();
// 获取响应体
const response = ctx.getResponse<Response>();
// 获取状态码
const statusCode = exception.getStatus();
// 自定义异常返回体
response.status(statusCode).json(responseMessage(null, exception.message, statusCode));
}
}
全局异常过滤器
创建一个全局异常过滤器来处理所有的异常,并将其转换为统一的响应格式。
在 src
目录中新建 /filter/all-exception.filter.ts
文件:
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { Response } from 'express';
import { responseMessage } from '@/utils';
// @Catch() 装饰器绑定所需的元数据到异常过滤器上。它告诉 Nest这个特定的过滤器正在寻找
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// 获取上下文
const ctx = host.switchToHttp();
// 获取响应体
const response = ctx.getResponse<Response>();
// 获取状态码,判断是HTTP异常还是服务器异常
const statusCode =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
// 自定义异常返回体
response
.status(statusCode)
.json(responseMessage(null, '服务器内部错误!', statusCode));
}
}
全局配置
在 main.ts
中注册全局的异常过滤器。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from '@/filter/all-exception.filter'; // 全局异常过滤器
import { HttpExceptionsFilter } from '@/filter/http-exception.filter'; // http 异常过滤器
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 错误异常捕获 和 过滤处理
app.useGlobalFilters(new AllExceptionsFilter());
app.useGlobalFilters(new HttpExceptionsFilter());
await app.listen(3000);
}
bootstrap();
效果预览
-
正常请求成功
-
当我们访问一个不存在的接口时