玩转Nest项目中统一返回格式

介绍

在nest企业级开发项目中,必不可免的就是封装统一返回格式,否则返回格式一团糟,对应的前端也要忍不住给你唱一段rep,那么在nest中,你有两种方式来实现这个功能,一种是使用利用拦截器来实现,另一种是自己封装一个类来实现,两者皆可,不过在这里还是推荐使用类来实现,原因就是足够灵活,那么话不多说,让我们来实现下这两种方式。

拦截器实现

在nest官网中可以看到,在拦截器这一章节中,给出了响应映射这样的功能案例,并且在开篇也说明了,使用拦截器需要实现 NestInterceptor 接口类,很多道友不明白什么叫做实现 NestInterceptor 接口类,其实 NestInterceptor 就是一个ts的类型声明,nest内部对此方法已经做了处理,你想使用,就按照给出的接口声明来照做实现就行,那接下来我们就按照官网来实现下

我们在src下创建一个transform.interceptor.ts文件,并根据官网给出的示例代码先尝试一下,如下:

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) => ({ data })));
    }
  }

然后将刚刚创建的拦截器来绑定一下,在getHello接口绑定使用:

让我们来请求一下看看现在的返回格式是什么样子的:

可以看到,现在返回格式中包了一层 data ,这样是不是跟我们常见的接口返回格式一样了呢?

但是现在还少了点东西,正常我们接口返回还有 code、message 字段,那这怎么添加呢?其实非常简单,让我们把 TransformInterceptor 稍作处理即可,如下:

这里将map返回值手动定义了一下,增加了code、message字段,注意:上面的类型也要对应增加,现在再让我们来看下返回的格式是怎样的吧,如下:

没错,这就是我们想要的结果,但是现在code、message两个字段是写死的,按理来说应该是动态的才对,假设我想返回 code: 400,message: 请求失败,这怎么办呢?很简单,我们需要在controller中返回对应的数据,然后在 TransformInterceptor 中手动接收下数据即可,现在我们getHello接口返回的是传过来的请求参数,现在我们要额外再返回两个字段,如下所示:

第一步:controller返回对应的数据格式

让我们来看下现在的返回结果:

显然不是我们理想中的格式,接下来还有第二步,如下所示:

我们先来打印下TransformInterceptor中data参数:

可以看到,在data中,成功打印出了我们在controller中返回的对象,那这样就好办了,我们只需要取出对应的值即可:

再来请求下接口看下:

完美!

自定义类实现(推荐)

虽然我们上面使用拦截器实现了统一 返回格式功能,但是并不是很舒服,还是有局限性,没有自定义类来拓展性高,那么接下来我们就来用自定义类来实现:

我在先创建一个result.ts文件,里面去实现我们的ResultData这样一个类:

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

export class ResultData {
  constructor(
    public code = HttpStatus.OK,
    public msg?: string,
    public data?: any,
  ) {
    this.code = code;
    this.msg = msg || '操作成功';
    this.data = data || null;
  }

  static success(data?: any, msg?: string) {
    return new ResultData(HttpStatus.OK, msg, data);
  }

  static fail(code = HttpStatus.BAD_REQUEST, msg?: string, data?: any) {
    return new ResultData(code, msg, data);
  }
}

接下来我们在app.controller.ts中去使用一下:

import {
  Body,
  Controller,
  Post,
  UseInterceptors,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { AppService } from './app.service';
import { CreateUserDto } from './create-user.dto';
import { TransformInterceptor } from './transform.interceptor';
import { ResultData } from './result';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Post()
  // @UseInterceptors(TransformInterceptor)
  getHello(
    @Body(new ValidationPipe({ transform: true })) createUserDto: CreateUserDto,
  ) {
    return ResultData.success(createUserDto, '啦啦啦');
  }
}

注意:我们在使用的时候需要注释掉我们之前编写的统一返回拦截器,否则的话,我们在 ResultData 中返回后,又回进入拦截器再次组装返回格式,接下来我们来请求一下:

可以看到,已经正确的返回了,当然了 ,我们在实际项目开发中,单单这一种封装还是有点单一的,不过其他的我们就按照我们的需求自己封装即可

注意REST API

因为我们使用的是REST API风格,所以对于post请求默认返回的是201,所以需要手动处理成200,不然前端如果只认200的话,是会报错的,可能这么一个小问题就是一上午时间(焦头烂额),

那么怎么办呢 ?

  1. 我们需要从 Request 请求中取出 method 判断是否是post请求
  2. 我们需要再从 Response 中取出 statusCode 判断是否等于 201

纯拦截器实现统一返回格式

纯拦截器实现统一返回格式代码修改如下:

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

export interface ResponseType<T> {
  data: T;
  code: number;
  message: string;
}

@Injectable()
export class TransformInterceptor<T>
  implements NestInterceptor<T, ResponseType<T>>
{
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<ResponseType<T>> {
    // 因为nestjs使用REST API风格,对于post请求默认返回201,所以需要手动处理成200
    const request = context.switchToHttp().getRequest<Request>();
    const response = context.switchToHttp().getResponse<Response>();
    if (
      request.method === 'POST' &&
      response.statusCode === HttpStatus.CREATED
    ) {
      response.status(HttpStatus.OK);
    }

    return next.handle().pipe(
      map((data) => {
        console.log(data);

        return {
          data: data.data,
          code: data.code,
          message: data.message,
        };
      }),
    );
  }
}

至于 RequestResponse类型,因为我们项目使用的是express,所以从 express 中导入这两个接口。

自定义实现类统一返回格式

result.ts不用修改,需要添加拦截器:
 

import {
  Injectable,
  NestInterceptor,
  CallHandler,
  HttpStatus,
  ExecutionContext,
} from '@nestjs/common';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { ResultData } from './result';
import { Request, Response } from 'express';

@Injectable()
export class ResponsInterceptoreBase<T> implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<ResultData> {
    // 因为nestjs使用REST API风格,对于post请求默认返回201,所以需要手动处理成200
    const request = context.switchToHttp().getRequest<Request>();
    const response = context.switchToHttp().getResponse<Response>();
    
    if (
      request.method === 'POST' &&
      response.statusCode === HttpStatus.CREATED
    ) {
      response.status(HttpStatus.OK);
    }
    return next.handle().pipe(
      map((data:ResultData) => {
        return data
      }),
    );
  }
}

在 app.controller.ts 中使用拦截器:

import {
  Body,
  Controller,
  Post,
  UseInterceptors,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { AppService } from './app.service';
import { CreateUserDto } from './create-user.dto';
import { TransformInterceptor } from './transform.interceptor';
import { ResultData } from './result';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Post()
  @UseInterceptors(TransformInterceptor)
  getHello(
    @Body(new ValidationPipe({ transform: true })) createUserDto: CreateUserDto,
  ) {
    return ResultData.success(createUserDto, '啦啦啦');
  }
}

我们请求一下:

这样我们就可以愉快的编写接口代码了~

文章转自:https://juejin.cn/post/7376484708547346443

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值