企业网站源码 多语言,长春房产网,建网站怎样才能通过备案,网络设计专业介绍返回格式化拦截器
在上一篇《Nest.js权限管理系统开发#xff08;四#xff09;Swagger API接入》中#xff0c;我们在base.controller.ts中创建了多个接口#xff0c;每个接口都有不同的返回类型。现实中我们往往需要统一返回数据的格式#xff0c;例如#xff1a;
{四Swagger API接入》中我们在base.controller.ts中创建了多个接口每个接口都有不同的返回类型。现实中我们往往需要统一返回数据的格式例如
{code: 200,msg: ok,data: This action updates a #admin user
}
next.js中我们可以通过返回格式拦截器对请求成功(状态码为 2xx)的数据进行一个格式化同样的先执行
nest g interceptor common/interceptor/transform
创建一个拦截器,按照官网示例给的复制过来
import { CallHandler, ExecutionContext, NestInterceptor, Injectable } from nestjs/common
import { Observable } from rxjs
import { map } from rxjs/operators
import { ResultData } from src/common/utils/resultInjectable()
export class TransformInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandlerany): Observableany | PromiseObservableany {const req context.getArgByIndex(1).reqreturn next.handle().pipe(map((data) {return ResultData.ok(data)}),)}
}
在main.ts中注册
import { TransformInterceptor } from ./common/interceptor/transform/transform.interceptor;app.useGlobalInterceptors(new TransformInterceptor());
返回异常过滤器
自定义HttpException
这样做之后我们会发现请求成功的 code 只能是 200,一般项目中请求成功还需要很多业务异常状态码返回给前端,所以我们需要新建一个抛出业务异常的类ApiException 我们先创建common/enums/code.enum.ts用于存放我们的业务状态码,这里简单写几个
export enum ApiErrorCode {/** 公共错误 *//** 服务器出错 */SERVICE_ERROR 500500,/** 数据为空 */DATA_IS_EMPTY 100001,/** 参数有误 */PARAM_INVALID 100002,
}
在common/filter/http-exception下新建api.exception.ts创建一个ApiException类继承HttpException,接受三个参数错误信息,错误码code,http状态码(默认是200)
import { HttpException, HttpStatus } from nestjs/common;
import { ApiErrorCode } from ../../enum/code.enum;export class ApiException extends HttpException {private errorMessage: string;private errorCode: ApiErrorCode;constructor(errorMessage: string,errorCode: ApiErrorCode,statusCode: HttpStatus HttpStatus.OK,) {super(errorMessage, statusCode);this.errorMessage errorMessage;this.errorCode errorCode;}getErrorCode(): ApiErrorCode {return this.errorCode;}getErrorMessage(): string {return this.errorMessage;}
}
然后我们可以在需要的地方抛出相应的异常了。
异常过滤器
抛出异常不是异常请求的最终归宿。当我们使用 NestJS 内置的异常处理HttpException比如
throw new HttpException(您无权登录, HttpStatus.FORBIDDEN);或者上面我们创建的ApiException客户端就会收到
{statusCode: 403,message: 您无权登录
}但是这样不够灵活所以我们可以新建一个异常过滤器进行自定义的操作
nest g filter common/filter/http-exception
然后修改common/filter/http-exception/http-exception.filter.ts
import {ExceptionFilter,Catch,ArgumentsHost,HttpException,
} from nestjs/common;
import { Request, Response } from express;Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx host.switchToHttp();const response ctx.getResponseResponse();const request ctx.getRequestRequest();const status exception.getStatus();if (exception instanceof ApiException) {response.status(status).json({code: exception.getErrorCode(),msg: exception.getErrorMessage(),});return;}response.status(status).json({code: status,timestamp: new Date().toISOString(),path: request.url,msg: exception.message,});}
}
最后在main.ts中进行注册
import { HttpExceptionFilter } from ./common/filter/http-exception/http-exception.filter;app.useGlobalFilters(new HttpExceptionFilter());除了HttpException我们也要过滤普通异常
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from nestjs/commonCatch()
export class ExceptionsFilter implements ExceptionFilter {catch(exception: any, host: ArgumentsHost) {const ctx host.switchToHttp()const response ctx.getResponse()const request ctx.getRequest()const status exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERRORresponse.status(status).json({code: status,msg: Service Error: ${exception},})}
}
Swagger返回类型修复
前面我们已经对返回正常数据进行格式化并拦截处理了异常发生时的返回数据格式。因为我们通过拦截器对返回数据进行了包裹那么在我们的接口里我们只需要返回data部分即可不需要创建并返回各种Response类了。但是这里有个问题由于 TypeScript 不存储有关泛型或接口的元数据因此当你在 DTO 中使用它们时SwaggerModule 可能无法在运行时正确生成模型定义。所以我们前面并没有采用类似下面的类作为我们的返回类型
import { ApiProperty } from nestjs/swaggerexport class ResultDataT {constructor(code 200, msg?: string, data?: T) {this.code codethis.msg msg || okthis.data data || undefined}ApiProperty({ type: number, default: 200 })code: numberApiProperty({ type: string, default: ok })msg?: stringApiProperty()data?: T
}
回到我们的例子中要在不创建LoginResponse只创建它的data的类型的情况下如何实现等同于下面效果的Swagger注解 ApiOkResponse({ description: 登录成功返回, type: LoginResponse })
我们需要自定义一个装饰器创建common/decorators/result.decorator.ts
import { Type, applyDecorators } from nestjs/common
import { ApiExtraModels, ApiOkResponse, getSchemaPath } from nestjs/swagger
import { ResultData } from ../utils/resultconst baseTypeNames [String, Number, Boolean]
/*** 封装 swagger 返回统一结构* 支持复杂类型 { code, msg, data }* param model 返回的 data 的数据类型* param isArray data 是否是数组* param isPager 设置为 true, 则 data 类型为 { list, total } , false data 类型是纯数组*/
export const ApiResult TModel extends Typeany(model?: TModel, isArray?: boolean, isPager?: boolean) {let items nullconst modelIsBaseType model baseTypeNames.includes(model.name)if (modelIsBaseType) {items { type: model.name.toLocaleLowerCase() }} else if(model) {items { $ref: getSchemaPath(model) }}let prop {}if (isArray isPager) {prop {type: object,properties: {list: {type: array,items,},total: {type: number,default: 0,},},}} else if (isArray) {prop {type: array,items,}} else if (items) {prop items} else {prop { type: null, default: null }}return applyDecorators(ApiExtraModels(...(model !modelIsBaseType ? [ResultData, model] : [ResultData])),ApiOkResponse({schema: {allOf: [{ $ref: getSchemaPath(ResultData) },{properties: {data: prop,},},],},}),)
}
然后将ApiResult装饰器应用到接口方法上 Post(login)ApiOperation({ summary: 登录 })ApiResult(CreateTokenDto)async login(Body() dto: LoginUser): PromiseCreateTokenDto {return await this.userService.login(dto.account, dto.password)}
重新运行项目我们看到返回数据已经显示完整了