引言
一般我们都会返回统一的响应格式,无论是正确还是错误的接口,所以在nest中使用拦截器和异常过滤器实现 RESTful API的统一响应格式,直接进入正题
主题
响应拦截器
在src下创建interceptors/transform.interceptor.ts,也可以用命令nest g in interceptors/transform
typescript
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface ResponseResult<T> {
code: number;
message: string;
data: T;
timestamp: number;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, ResponseResult<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<ResponseResult<T>> {
return next.handle().pipe(
map(data:T => ({
code: 200,
message: '请求成功',
data,
timestamp: Date.now()
}))
);
}
}
然后到main.ts,去全局实例化
javascript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { TransformInterceptor } from './interceptors/transform.interceptor';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 注册全局拦截器
app.useGlobalInterceptors(new TransformInterceptor());
await app.listen(3000);
}
bootstrap();
创建个user模块,nest g res user --no-spec
,我们去user模块写个接口试试
typescript
import { Controller, Get, Post, Body, Param, HttpException, HttpStatus } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
// 直接返回数据,拦截器会处理格式
return this.userService.findAll();
}
}
结果是这样的,证明我们响应拦截返回成功了,然后也可以把时间转换成YYYY/MM/DD这种,自由选择
css
{
"code": 200,
"message": "请求成功",
"data": [...],
"timestamp": 1647434353255
}
异常过滤器
在src下创建filters/httpExecption.filter.ts,也可以用命令nest g f filters/httpExecption
ini
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
let message = exception.message || '服务器内部错误';
// 如果是HTTP异常,尝试获取更详细的错误信息
if (exception instanceof HttpException) {
const exceptionResponse = exception.getResponse();
if (typeof exceptionResponse === 'object' && 'message' in exceptionResponse) {
message = exceptionResponse['message'];
}
}
response.status(status).json({
code: status,
message,
timestamp: Date.now(),
path: request.url,
data: null
});
}
}
然后到main.ts,去全局实例化 app.useGlobalFilters(new HttpExceptionFilter());
我们在去user模块写个请求,可以看到httpStatus其实有很多选项,根据情况去描述
我们在去postman试下接口
typescript
import { Controller, Get, Post, Body, Param, HttpException, HttpStatus } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
// 直接返回数据,拦截器会处理格式
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
const user = this.userService.findOne(id);
if (!user) {
// 抛出异常,异常过滤器会处理
throw new HttpException('用户不存在', HttpStatus.NOT_FOUND);
}
return user;
}
}
返回是这样的,证明异常处理也成功了
json
{
"code": 404,
"message": "用户不存在",
"data": null,
"timestamp": 1647434367890,
"path": "/users/123"
}
扩展
后面会研究一下grapql的响应拦截写法,好像不太一样,学起来有点难,马上到