在 Nest.js 发送之前如何格式化响应?



我遵循了文档,并能够为响应映射添加一个拦截器。

我想要一致的 json 格式输出来响应。

我如何使用拦截器或其他比这种方法更好的方法来实现这一点。

{
"statusCode": 201,
"message": "Custom Dynamic Message"
"data": {
// properties
meta: {}
}
}

transform.interceptor.ts

import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response<T> {
statusCode: number;
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) => ({
statusCode: context.switchToHttp().getResponse().statusCode,
data,
})),
);
}
}

app.controller.ts

export class AppController {
@Post('login')
@UseGuards(AuthGuard('local'))
@ApiOperation({ summary: 'Login user' })
@ApiBody({ type: LoginDto })
@ApiOkResponse({ content: { 'application/json': {} } })
@UseInterceptors(TransformInterceptor)
async login(@Request() req) {
const result = await this.authService.login(req.user);
return { message: 'Thank you!', result };
}
}

如果我了解您对控制器响应和整体拦截器响应的作用,您可以做的类似:

import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response<T> {
statusCode: number;
message: string;
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) => ({
statusCode: context.switchToHttp().getResponse().statusCode,
message: data.message
data: {
result: data.result,
meta: {} // if this is supposed to be the actual return then replace {} with data.result
}
})),
);
}
}

并保持您的控制器返回为{message: 'Custom message', result}

另一种需要更多前期但可能允许更干净的代码的选项是创建一个自定义装饰器,该装饰器反映来自类和方法的值(消息(,然后在注入反射器后在拦截器中检索该值,但同样,这将需要更多的前期工作来设置。

我没有要求每个控制器始终在响应数据中包含消息属性,而是利用反射器和SetMetadata来提取消息值并将其设置为控制器方法上的元数据。

response_message.decorator.ts

import { SetMetadata } from '@nestjs/common';
export const ResponseMessage = (message: string) =>
SetMetadata('response_message', message);

response.interceptor.ts

import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response<T> {
statusCode: number;
message: string;
data: T;
}
@Injectable()
export class TransformInterceptor<T>
implements NestInterceptor<T, Response<T>>
{
constructor(private reflector: Reflector) {}
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<Response<T>> {
return next.handle().pipe(
map((data) => ({
statusCode: context.switchToHttp().getResponse().statusCode,
message:
this.reflector.get<string>(
'response_message',
context.getHandler(),
) || '',
data,
})),
);
}
}

控制器中的用法

media.controller.ts

@Get('/stats')
@ResponseMessage('Fetched Stats Succesfully')
getUserMediaStats(@GetUser('id') userId: Types.ObjectId) {
return this.mediaService.getUserMediaStats(userId);
}

我的响应结构

{
"statusCode": 200,
"message": "Fetched Stats Succesfully",
"data": {
"userId": "6419cbb6c053f0692ef400ae",
"totalCount": 8,
"totalSizeMB": 2.079585,
"imageCount": 8,
"videoCount": 0
}
}

我尝试了上述两个答案

src\interceptors\transform.interceptor.ts

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { map, Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
export interface Response<T> {
statusCode: number;
message: string;
data: T;
}
@Injectable()
export class TransformationInterceptor<T> implements NestInterceptor<T, 
Response<T>> {
constructor(private reflector: Reflector) {}
intercept(context: ExecutionContext, next: CallHandler): 
Observable<Response<T>> {
return next.handle().pipe(
map((data) => ({
message: this.reflector.get<string>('response_message', 
context.getHandler()) || data.message || '',
statusCode: context.switchToHttp().getResponse().statusCode,
data: data.result || data
}))
);
}
}

src\employees\employees.controller.ts

@Get()
@ResponseMessage('Employees records fetched Succesfully')
findAll() {
return this.employeesService.findAll();
}

响应结构

{
"message": "Employees records fetched Succesfully",
"statusCode": 200,
"data": [
{
"id": 1,
"code": "2001",
"companyCode": "01",
"firstName": "Walter",
"lastName": "Mendoza",
"email": "w.dummy@gmail.com",
"phone": "+1214",
"siteId": 4,
"roleId": 5,
"isActive": 0,
"inviteStatus": 3,
"shiftId": null,
"createdAt": "2019-08-26T15:21:44.000Z",
"syncAt": "2022-10-09T19:01:40.000Z",
"modifiedAt": "2022-10-09T19:01:40.000Z"
},
{
"id": 2,
"code": "2002",
"companyCode": "01",
"firstName": "Hugo",
"lastName": "Rosa",
"email": "dummy@gmail.com",
"phone": "+18324888233",
"siteId": 4,
"roleId": 5,
"isActive": 1,
"inviteStatus": 3,
"shiftId": null,
"createdAt": "2019-08-26T15:21:44.000Z",
"syncAt": "2022-10-09T19:01:40.000Z",
"modifiedAt": "2022-10-09T19:01:40.000Z"
}
] 
}

@Get(':id')
async findOne(@Param('id') id: string) {
const result = await this.employeesService.findOne(+id);
return { message: `Employees ${id} detail fetched Succesfully`, 
result};
}

响应结构

{
"message": "Employees 1 detail fetched Succesfully",
"statusCode": 200,
"data": {
"id": 1,
"code": "2001",
"companyCode": "01",
"firstName": "Walter",
"lastName": "Mendoza",
"email": "dummy@gmail.com",
"phone": "+1214",
"siteId": 4,
"roleId": 5,
"isActive": 0,
"inviteStatus": 3,
"shiftId": null,
"createdAt": "2019-08-26T15:21:44.000Z",
"syncAt": "2022-10-09T19:01:40.000Z",
"modifiedAt": "2022-10-09T19:01:40.000Z"
}
}

最新更新