使用自定义拦截器进行响应

Gor*_*lla 5 interceptor nest

我正在使用全局拦截器来获取如下响应:

{
  "data": "",
  "statusCode": int
  "message": "string"
}
Run Code Online (Sandbox Code Playgroud)

所以我创建了拦截器文件

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

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

@Injectable()
export class TransformationInterceptor<T> implements NestInterceptor<T, Response<T>> {
    intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
        return next.handle().pipe(map(data => ({ 
            data: data,
            statusCode: context.switchToHttp().getResponse().statusCode,
            message: data.message
        })));
    }
}
Run Code Online (Sandbox Code Playgroud)

并将其放入我的main.ts

在我的控制器中我有:

  @Patch('/:userId')
  @HttpCode(201)
  public async updateUser(    
    @Param('userId') userId: string,
    @Body() userUpdate: UpdateUserDto): Promise<any> {      
    return await this.usersService.update(userId, userUpdate);    
  }
Run Code Online (Sandbox Code Playgroud)

结果是:

{
  "data": {
    "_id": "621d07d9ea0cdc600fae0f02",    
    "username": "foo",
    "name": "stringwwww",
    "__v": 0
  },
  "statusCode": 201
}
Run Code Online (Sandbox Code Playgroud)

如果我想添加自定义消息,我需要返回一个对象,例如:

@Patch('/:userId')
  @HttpCode(201)
  public async updateUser(    
    @Param('userId') userId: string,
    @Body() userUpdate: UpdateUserDto): Promise<any> {      
    const result = await this.usersService.update(userId, userUpdate);    
    return { message: 'User updated', result };    
  }
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,我有两次消息,并且结构不正确:

{
  "data": {
    "message": "User updated",
    "result": {
      "_id": "621d07d9ea0cdc600fae0f02",
      "username": "foo",
      "name": "stringwwww",
      "__v": 0
    }
  },
  "statusCode": 201,
  "message": "User updated"
}
Run Code Online (Sandbox Code Playgroud)

如何设置自定义(可选)消息?

I can modify my interceptors like:
@Injectable()
export class TransformationInterceptor<T> implements NestInterceptor<T, Response<T>> {
    intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
        return next.handle().pipe(map(data => ({ 
            data: data.res,
            statusCode: context.switchToHttp().getResponse().statusCode,
            message: data.message
        })));
    }
}
Run Code Online (Sandbox Code Playgroud)

我的控制器喜欢:

@Patch('/:userId')
  @HttpCode(201)
  public async updateUser(    
    @Param('userId') userId: string,
    @Body() userUpdate: UpdateUserDto): Promise<any> {      
    const result = await this.usersService.update(userId, userUpdate);    
    return { message: 'User updated', res: result };    
  }
Run Code Online (Sandbox Code Playgroud)

我会得到正确的表格,但我不想添加

return { message: 'User updated', res: result };    
Run Code Online (Sandbox Code Playgroud)

对于每个控制器

小智 2

实现此目的的一种方法如下,但每个控制器都必须绑定固定消息

创建响应装饰器(response.decorator.ts)

    import { SetMetadata } from '@nestjs/common'
    
    export const ResponseMessageKey = 'ResponseMessageKey'
    export const ResponseMessage = (message: string) => SetMetadata(ResponseMessageKey, message)
Run Code Online (Sandbox Code Playgroud)

为您的响应创建一个常量文件 (response.constants.ts)

    export const USER_INSERTED = 'User Inserted'
    export const USER_UPDATED = 'User Updated'
    export const USER_DELETED = 'User Deleted'
Run Code Online (Sandbox Code Playgroud)

将装饰器添加到控制器以设置响应消息元数据

    @Patch('/:userId')
    @HttpCode(201)
    @ResponseMessage(USER_UPDATED)
    public async updateUser(    
      @Param('userId') userId: string,
      @Body() userUpdate: UpdateUserDto
    ): Promise<any> {      
      const result = await this.usersService.update(userId, userUpdate);    
      return result;    
    }
Run Code Online (Sandbox Code Playgroud)

更新您的拦截器以从控制器上设置的元数据读取响应消息并将其添加到响应中

    import { Reflector } from '@nestjs/core'

    @Injectable()
    export class TransformationInterceptor<T>
      implements NestInterceptor<T, Response<T>>
    {
      constructor(private reflector: Reflector) {}

      intercept(
        context: ExecutionContext,
        next: CallHandler
      ): Observable<Response<T>> {
        const responseMessage = this.reflector.get<string>(
          ResponseMessageKey,
          context.getHandler()
        ) ?? ''

        return next.handle().pipe(
          map((data) => ({
            data,
            statusCode: context.switchToHttp().getResponse().statusCode,
            message: responseMessage
          }))
        )
      }
    }
Run Code Online (Sandbox Code Playgroud)

您可以扩展此方法以将字符串/对象列表设置为可能的响应(元数据),并根据拦截器中的响应代码发送特定消息response.message