Nest.js权限管理系统开发(五)返回格式化

返回格式化拦截器

在上一篇《Nest.js权限管理系统开发(四)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/result'@Injectable()
export class TransformInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> | Promise<Observable<any>> {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.getResponse<Response>();const request = ctx.getRequest<Request>();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/common'@Catch()
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/swagger'export class ResultData<T> {constructor(code = 200, msg?: string, data?: T) {this.code = codethis.msg = msg || 'ok'this.data = data || undefined}@ApiProperty({ type: 'number', default: 200 })code: number@ApiProperty({ type: 'string', default: 'ok' })msg?: string@ApiProperty()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/result'const 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 Type<any>>(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): Promise<CreateTokenDto> {return await this.userService.login(dto.account, dto.password)}

重新运行项目,我们看到返回数据已经显示完整了:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/702863.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

conda 导出/导出配置好的虚拟环境

一. 导出环境配置&#xff08;yml文件&#xff09; 1. 在主目录下激活虚拟环境&#xff08;UE4是我的虚拟环境名称&#xff0c;请根据你自己的名称进行修改&#xff09; conda activate UE4 2. 运行此代码 conda env export > environment.yml 二. 导入环境配置&#xf…

python统计分析——多解释变量的方差分析

参考资料&#xff1a;用python动手学统计学 1、导入库 # 导入库 # 用于数值计算的库 import numpy as np import pandas as pd import scipy as sp from scipy import stats # 用于绘图的库 from matplotlib import pyplot as plt import seaborn as sns sns.set() # 用于估计…

Linux基础命令—进程管理

基础知识 linux进程管理 什么是进程 开发写代码->代码运行起来->进程 运行起来的程序叫做进程程序与进程区别 1.程序是一个静态的概念,主要是指令集和数据的结合,可以长期存放在操作系统中 2.进程是一个动态的概念,主要是程序的运行状态,进程存在生命周期,生命周期结…

Seata分布式事务实战XATCC模式

目录 XA模式 XA 模式的使用 Spring Cloud Alibaba整合Seata XA TCC模式 TCC模式接口改造 TCC如何控制异常 Spring Cloud Alibaba整合Seata TCC XA模式 整体机制 在 Seata 定义的分布式事务框架内&#xff0c;利用事务资源&#xff08;数据库、消息服务等&#xff09;对…

【Python从入门到进阶】49、当当网Scrapy项目实战(二)

接上篇《48、当当网Scrapy项目实战&#xff08;一&#xff09;》 上一篇我们正式开启了一个Scrapy爬虫项目的实战&#xff0c;对当当网进行剖析和抓取。本篇我们继续编写该当当网的项目&#xff0c;讲解刚刚编写的Spider与item之间的关系&#xff0c;以及如何使用item&#xff…

【python】0、超详细介绍:json、http

文章目录 一、json二、http2.1 json 读取 request 序列化 三、基本类型3.1 decimal 四、图像4.1 颜色格式转换 一、json import json f open(data.json) # open json file data json.load(f) # 读出 json object for i in data[emp_details]: # 取出一级属性 emp_details, …

云尚办公-0.3.0

5. controller层 import pers.beiluo.yunshangoffice.model.system.SysRole; import pers.beiluo.yunshangoffice.service.SysRoleService;import java.util.List;//RestController&#xff1a;1.该类是控制器&#xff1b;2.方法返回值会被写进响应报文的报文体&#xff0c;而…

ChatRTX安装教程

介于本人一直想将现有的智慧城市的文档结合大模型RAG实现知识库问答助手&#xff0c;借着Chat With RTX的风潮正好将机器人和知识库合二为一&#xff0c;方便以后对众多文件进行查阅。 一、概要 Chat With RTX 是一个 Demo&#xff0c;用来将您自己的资料&#xff08;文档、笔…

第三节:kafka sarama 遇到Bug?

文章目录 前言一、先上结果二、刨根问底总结 前言 前面两节&#xff0c;我们已经简单应用了sarama的两个类型Client和ClusterAdmin&#xff0c;其中有一个案例是获取集群的ControllerId&#xff0c;但是在后面的测试过程过程中&#xff0c;发现一个问题&#xff0c;返回的Cont…

【PyQt5桌面应用开发】3.Qt Designer快速入门(控件详解)

一、Qt Designer简介 Qt Designer是PyQt程序UI界面的实现工具&#xff0c;可以帮助我们快速开发 PyQt 程序的速度。它生成的 UI 界面是一个后缀为 .ui 的文件&#xff0c;可以通过 pyiuc 转换为 .py 文件。 Qt Designer工具使用简单&#xff0c;可以通过拖拽和点击完成复杂界面…

仿12306校招项目业务二(列车检索)

目录 验证数据 加载城市数据 查询列车站点信息 查询列车余票信息 构建列车返回数据 12306 项目中列车数据检索接口路径 &#xfeff; TicketController的pageListTicketQuery&#xfeff;。 GetMapping("/api/ticket-service/ticket/query")public Result<T…

查看笔记本电池健康状态-windows11

在 Windows 11 中获取详细的电池报告 Windows 11 中内置的 Powerfg 命令行选项来生成电池报告。 在任务栏上选择“搜索”&#xff0c;键入“cmd”&#xff0c;长按&#xff08;或右键单击&#xff09;“命令提示符”&#xff0c;然后选择“以管理员身份运行” ->“是”。 …

Mac使用K6工具压测WebSocket

commend空格 打开终端&#xff0c;安装k6 brew install k6验证是否安装成功 k6 version设置日志级别为debug export K6_LOG_LEVELdebug执行脚本&#xff08;进入脚本所在文件夹下&#xff09; k6 run --vus 100 --duration 10m --out csvresult.csv script.js 脚本解释&…

自定义神经网络三之梯度和损失函数激活函数

文章目录 前言梯度概述梯度下降算法梯度下降的过程 optimize优化器 梯度问题梯度消失梯度爆炸 损失函数常用的损失函数损失函数使用原则 激活函数激活函数和损失函数的区别激活函数Relu-隐藏层激活函数Sigmoid和Tanh-隐藏层Sigmoid函数Tanh&#xff08;双曲正切&#xff09; &l…

【前端】nginx 反向代理,实现跨域问题

前面讲跨域的问题&#xff0c;这篇 C# webapi 文章里面已经说过了。在上述文章中是属于从服务器端去允许访问的策略去解决跨域问题。而这里是从客户端的角度利用反向代理的方法去解决跨域问题。 反向代理&#xff1a;其原理就是将请求都接收到一个中间件&#xff08;中间地址&a…

IO 作业 24/2/26

1>思维导图 1> 使用消息队列完成两个进程间相互通信 #include<myhead.h> //定义一个消息类型 struct msgbuf {long mtype; //消息类型char mtext[1024]; //消息正文 }; //定义一个宏&#xff0c;表示消息正文大小 #define MSGSIZE sizeof(struct msgbuf…

人工智能 — 点云模型

目录 一、点云模型1、三维图像2、点云1、概念2、内容 3、点云处理的三个层次1、低层次处理方法2、中层次处理方法3、高层次处理方法 二、Spin image 一、点云模型 1、三维图像 三维图像是一种特殊的信息表达形式&#xff0c;其特征是表达的空间中三个维度的数据。 和二维图像…

荣耀手机如何开启地震预警功能

1、打开荣耀手机&#xff0c;进入“设置”&#xff0c;在搜素栏输入“地震”。 2、进入“安全-应急预警通知”功能栏。 3、开启“地震预警”。 4、查看“预警演示教程”。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e207e356bb634c11adf926c6a53e48cc.png…

Mysql学习之事务日志redolog深入剖析

Mysql 事务日志 redo log 事务有4种特性&#xff1a;原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现的呢&#xff1f; 事务的隔离性由锁机制实现。而事务的原子性、一致性和持久性由事务的redo日志和undo日志来保证。 REDO LOG 称为重做日志&…

OpenGL ES (OpenGL) Compute Shader 计算着色器是怎么用的?

OpenGL ES (OpenGL) Compute Shader 是怎么用的? Compute Shader 是 OpenGL ES(以及 OpenGL )中的一种 Shader 程序类型,用于在GPU上执行通用计算任务。与传统的顶点着色器和片段着色器不同,Compute Shader 被设计用于在 GPU 上执行各种通用计算任务,而不是仅仅处理图形…