这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天。我们 Bocchi 小组采用 NestJS 开发这次掘金站点的后端,这几天我们尝试编写了统一返回格式、异常处理模块,写篇文章来记录下实现过程。
仓库地址: github.com/Bocchi-Deve…
统一返回格式
为了前端方便处理数据,后端肯定需要将请求结果进行统一返回,通过协商我们小组定义的接口返回格式如下。
{
"code": 200,
"success": true,
"data": {
"name": "@bocchi/juejin-core",
"author": "Bocchi-Developers <https://suemor.com>",
"version": "0.0.1",
"homepage": "https://github.com/Bocchi-Developers/juejin-core#readme",
"issues": "https://github.com/Bocchi-Developers/juejin-core/issues"
}
}
代码实现如下
/* eslint-disable @typescript-eslint/consistent-type-imports */
import type { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import * as SYSTEM from '~/constants/system.constant'
export interface Response<T> {
data: T
}
@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, Response<T>> {
constructor(private readonly reflector: Reflector) {}
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<Response<T>> {
if (!context.switchToHttp().getRequest()) {
return next.handle()
}
const handler = context.getHandler()
const bypass = this.reflector.get<boolean>(
SYSTEM.RESPONSE_PASSTHROUGH_METADATA,
handler,
)
if (bypass) {
return next.handle()
}
return next.handle().pipe(
map((data) => {
return {
code: context.switchToHttp().getResponse().statusCode || 200,
success: true,
data,
}
}),
)
}
}
全局异常处理
为了使我们后端能够正常运行,进行全局异常是必须的,为了方便前端处理,我们的格式尽可能与统一返回格式相似,我们小组定义的格式如下。
{
"code": 403,
"success": false,
"message": "密码不正确"
}
代码具体实现如下
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
HttpStatus,
} from '@nestjs/common'
type myError = {
readonly status: number
readonly statusCode?: number
readonly message?: string
}
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse()
const status =
exception instanceof HttpException
? exception.getStatus()
: (exception as myError)?.status ||
(exception as myError)?.statusCode ||
HttpStatus.INTERNAL_SERVER_ERROR
const res = (exception as any).response
response
.status(status)
.type('application/json')
.send({
code: res?.statusCode || 500,
success: false,
message: res?.message || (exception as any)?.message || '未知错误',
})
}
}
注册
为了能够正常使用到这两个模块,我们需要到 app.module里进行注册,具体代码如下。
@Module({
imports: [
....
],
controllers: [AppController],
providers: [
{
provide: APP_FILTER,
useClass: AllExceptionsFilter,
},
{
provide: APP_INTERCEPTOR,
useClass: ResponseInterceptor,
},
],
})
export class AppModule {}