在NestJs中,如何统一接口返回的数据结构🙃🙃🙃

1,678 阅读3分钟

在 NestJS 或任何其他现代后端框架中统一接口返回的数据结构是一个非常好的实践,原因包括:

  1. 一致性:统一的数据结构使得前端开发者知道每次请求都会收到相同格式的响应,无论是成功还是失败。这减少了处理不同响应格式的复杂性。前端应用可以建立统一的响应处理逻辑,比如统一处理错误或解析数据。

  2. 维护性:后端代码更加整洁,因为所有的响应都遵循同样的模板或结构。当你需要添加新的功能或信息时,统一的结构使得在不破坏现有客户端代码的情况下扩展变得更加容易。

统一接口返回的数据结构提升了接口的一致性、可维护性和用户体验,同时简化了前后端的开发和协作。虽然它可能需要在项目初期投入更多的设计和开发工作,但长期来看,这是一个值得的投资。

在 NestJS 中,你可以通过几种方式来统一接口返回的数据结构:

  • 拦截器(Interceptors:可以创建一个全局拦截器来统一修改和标准化出站响应。

在 Nestjs 中实现

在 NestJS 中,拦截器提供了一种强大的方法来拦截和修改方法的入参和出参,包括对响应数据的格式进行统一处理。

首先我们创建一个文件并且创建一个类并使用 @Injectable() 装饰器标记它们:

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from "@nestjs/common";
import { FastifyReply } from "fastify";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { getReasonPhrase } from "http-status-codes";
import { instanceToPlain } from "class-transformer";

function getCurrentTimestamp(): number {
  return Date.parse(new Date().toString()) / 1000;
}

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    next: CallHandler<any>
  ): Observable<any> {
    // 获取Fastify的响应对象
    const response: FastifyReply = context
      .switchToHttp()
      .getResponse<FastifyReply>();

    return next.handle().pipe(
      map((originalData: any) => {
        if (
          originalData &&
          originalData.code &&
          originalData.message &&
          "data" in originalData
        ) {
          return originalData; // 如果是,直接返回
        }

        // 获取响应状态码
        const statusCode: number = response.statusCode;
        // 获取对应状态码的标准消息
        const message: string = getReasonPhrase(statusCode);

        // 构造标准响应格式
        return {
          code: statusCode,
          message: message,
          data: instanceToPlain(originalData) || null,
          timestamp: getCurrentTimestamp(),
        };
      })
    );
  }
}

在上面的这段代码中,使用 pipe()和 map()来转换从控制器返回的数据。在 map 操作符中,它检查返回的数据是否已经具有特定的格式(包含 code, message, 和 data)。如果是,就直接返回这个数据;如果不是,就构造一个新的对象包含状态码、消息、数据和时间。

如果原始数据不符合预期的格式,拦截器将构建一个标准格式的响应对象,包含以下属性:

  • code: 响应的 HTTP 状态码。
  • message: 基于状态码的标准消息。
  • data: 原始数据或 null,转换为简单的 JavaScript 对象(如果原始数据是类的实例)。
  • timestamp: 当前的 UNIX 时间戳。

创建拦截器后,你需要将其注册到你的应用中。拦截器可以全局注册(影响所有路由)或针对特定路由注册。通常是在跟路由下全局注册:

import { APP_INTERCEPTOR } from "@nestjs/core";
import { TransformInterceptor } from "./TransformInterceptor.ts";

@Module({
  // ...
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: TransformInterceptor,
    },
    // ...
  ],
})
export class AppModule {}

这个时候,我们可以访问一下接口,获取一下数据,首先我们在 service 中是这样返回的:

20231222191655

在接口中访问,返回了这样的内容:

20231222191717

现在这样的内容就是已经被我们格式化完成了,之后返回的数据都是类似的了。

总结

通过使用拦截器,你可以有效地控制和统一 NestJS 应用中的接口响应格式,提升前后端的协同效率和用户体验。