【Express.js】使用zod检验

使用zod检验

上一节我们介绍了 express-validator,本节我们介绍一个更通用的检验工具 Zod

What’s Zod.js?

写前端的同学可能知道Zod,我们在提交表单前需要对数据初步检查,Zod是一个很棒的工具。前端可以偷懒,但后端不能偷懒,Zod也可以用到我们的 express 后端中来,封装一个 Zod 中间件即可

准备工作

用 evp-express-cli 创建一个最简洁的新项目。

了解Zod工作流程

  1. 定义待检验的数据格式
const { z } = requie("zod");const schema = z.object({username: z.string().nonempty("username cannot be empty")
})

这个 schema 就是 Zod 检验一个对象或者变量的检验器,如果检验目标只是一个值,z.string()之类的即可
2. 检验器验证传入
把待检查的数据传递给检验器,有4种:schema.parse(data), schema.parseSync(data), schema.safeParse(data), schema.safeParseSync(data),parse会直接抛出错误信息,而safeParse返回一个对象,包含了验证是否成功和错误信息,结构如是:{success: boolean, message: ZodError}
3. 错误处理
对于检验器发现的错误你需要自行处理

安装Zod

npm install zod

封装中间件

在 midwares 目录下创建 zod.js:
导出了一个 ZodValid函数:该函数传入一个对象,包含了 headers, params, query 和 body 四个可选属性,分别对应请求可以传入数据的四个部分,如果需要检验,就传入定义好的检验器给需要检验的部分;ZodValid会返回一个 request handler,在处理器里面根据传入的检验器分别去进行检验,这个处理器才是最后的中间件,ZodValid其实是一个中间件工厂。我在这里取出了第一个错误,并将错误的 message 抛出,evp-express 默认直接捕捉并返回错误信息,我这样写是为了让读者对错误信息能看的更清楚,非特定场景下,不一定要返回这样细致的错误信息,可以抛出统一错误信息

const { z } = require("zod");/*** @typedef {{* code: string;* expected: string;* received: any;* path: string[];* message: string;* }} MyZodError*//*** @param {z.ZodError} errors * @reutrns*/
function selFirstError(errors) {/*** @type {MyZodError[]}*/const errs = JSON.parse(errors);return errs[0];
}module.exports = {/*** * @param {{* headers: z.ZodObject|undefined;* params: z.ZodObject|undefined;* query: z.ZodObject|undefined;* body: z.ZodObject|undefined;* }} param0 * @returns*/ZodValid: ({headers, params, query, body})=>{/*** @type {import("express").RequestHandler}*/const handler = (req,res,next)=>{if (headers) {const result = headers.safeParse(req.headers);if (!result.success) {throw new Error(selFirstError(result.error).message)}}if (params) {const result = params.safeParse(req.params);if (!result.success) {throw new Error(selFirstError(result.error).message)}}if (query) {const result = query.safeParse(req.query);if (!result.success) {throw new Error(selFirstError(result.error).message)}}if (body) {console.log(req.body);const result = body.safeParse(req.body);if (!result.success) {throw new Error(selFirstError(result.error).message)}}next();}return handler;}
}

使用中间件

改写 router/index.js:定义2个路由,一个 GET 一个 POST,GET接口检验 query 参数种的 name 字段,POST接口检验请求体数据中的 name, pass 和 email,由于请求发送的数据格式使用了 Json,所以我们的ZodValid要放在转换请求体格式的Json中间件之后。

const { Router } = require('express');
const logger = require('../utils/logger');
const Resp = require('../model/resp');
const { ZodValid } = require('../midwares/zod');
const { z } = require('zod');
const { Json } = require('../midwares/bodyParser');const router = Router();router.get('/', ZodValid({query: z.object({ name: z.string().nonempty("name cannot be empty") })
}), async (req, res, next) => {const name = req.query.name;logger.info(`Hello World! ${name}`);res.json(Resp.ok(`Hello World! ${name}`, 1, null));
});router.post('/', Json, ZodValid({body: z.object({ name: z.string().nonempty("name cannot be empty").min(8, "name at least 8 length"),pass: z.string().nonempty("password cannot be empty").min(8, "password at least 8 lenght"),email: z.string().email("email is invalid") })
}), async (req, res, next) => {const name = req.body.name;logger.info(`Hello World! ${name}`);res.json(Resp.ok(`Hello World! ${name}`, 1, null));
});module.exports = router;

测试

调整请求数据,分别访问这两个接口,你将得到类似这样的结果:

{"code": 500,"msg": "name cannot be empty","data": null,"symbol": 0,"type": "Bad Request"
}
{"code": 500,"msg": "email is invalid","data": null,"symbol": 0,"type": "Bad Request"
}

本文仅演示了 Zod.js 最基础的用法,还有z.optional可选,z.nullish可空,z.refine自定义逻辑等api,更详细更高阶的用法可以查看 Zod官方手册:https://zod.dev/README_ZH

下一节-集成Redis

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

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

相关文章

Camunda 7.x 系列【10】使用 Java API 运行流程实例

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 前言2. 运行流程实例2.1 查询流程定义2.2 启动流程2.3 任务查询2.4 审批3. 数据表1. 前言…

vue3—SCSS的安装、配置与使用

SCSS 安装 使用npm安装scss: npm install sass sass-loader --save-dev 配置 配置到全局 🌟附赠代码🌟 css: {preprocessorOptions: {scss: {additionalData:import "./src/Function/Easy_I_Function/Echarts/ToSeeEcharts/utill.…

Spring Boot Admin 环境搭建与基本使用

Spring Boot Admin 环境搭建与基本使用 一、Spring Boot Admin是什么二、提供了那些功能三、 使用Spring Boot Admin3.1搭建Spring Boot Admin服务pom文件yml配置文件启动类启动admin服务效果 3.2 common-apipom文件feignhystrix 3.3服务消费者pom文件yml配置文件启动类control…

前端面试的性能优化部分(6)每天10个小知识点

目录 系列文章目录前端面试的性能优化部分(1)每天10个小知识点前端面试的性能优化部分(2)每天10个小知识点前端面试的性能优化部分(3)每天10个小知识点前端面试的性能优化部分(4)每天…

Simulation 线性静力分析流程

有限元仿真分析软件有很多,但是分析的流程却是大同小异,今天给大家分享的是Simulation的线性静力分析流程。 1.构思分析方案。 确定研究对象,研究的方法、验证方案等等。听起来比较空洞,实践过程中我建议首先需要把目标和有限元分…

HDFS中的Trash垃圾桶回收机制

Trash垃圾桶回收机制 文件系统垃圾桶背景功能概述Trash Checkpoint Trash功能开启关闭HDFS集群修改core-site.xml删除文件到trash删除文件跳过从trash中恢复文件清空trash 文件系统垃圾桶背景 回收站(垃圾桶)是windows操作系统里的一个系统文件夹&#…

C++学习笔记总结练习:并发编程与多线程

并发编程与多线程 1. 基础知识 C多线程 线程:线程是操作系统能够进行CPU调度的最小单位,它被包含在进程之中,一个进程可包含单个或者多个线程。可以用多个线程去完成一个任务,也可以用多个进程去完成一个任务,它们的…

一起学SF框架系列7.1-spring-AOP-基础知识

AOP(Aspect-oriented Programming-面向切面编程)是一种编程模式,是对OOP(Object-oriented Programming-面向对象编程)一种有益补充。在OOP中,万事万物都是独立的对象,对象相互耦合关系是基于业务进行的;但在…

python获取类名__qualname__,解决django接口ObjectDoesNotExist异常寻找model的问题

在django项目中,经常使用类似Model.objects.get(id1)的方法取对象,默认抛出的异常是ObjectDoesNotExist类型,通过try catch可以把异常捕获,获取的异常是Model.DoesNotExist类型, 要获知其类名,可以使用__na…

目标识别模型两种部署形态图

目标检测预训练模型基于新数据进行微调(训练)之后,得到一个权重文件。 在日常工业、车载等需求环境下,需要在嵌入式移动端的软件系统中调用该模型文件进行推断测试,软件系统追求性能经常使用C/C进行编码实现&#xff…

第十一次CCF计算机软件能力认证

第一题:打酱油 小明带着 N 元钱去买酱油。 酱油 10 块钱一瓶,商家进行促销,每买 3 瓶送 1 瓶,或者每买 5 瓶送 2 瓶。 请问小明最多可以得到多少瓶酱油。 输入格式 输入的第一行包含一个整数 N,表示小明可用于买酱油的…

聚合在Elasticsearch中的使用及示例验证

聚合在Elasticsearch中的使用 系统中使用的ES环境不一定每篇文章都有,但是可以在合集中找到,关注《醉鱼Java》一起进步 环境 elasticsearch 8.1 搭建 version: 3.8 services:cerebro:image: lmenezes/cerebro:0.8.3container_name: cerebroports:- "…

【深度学习】【风格迁移】Visual Concept Translator,一般图像到图像的翻译与一次性图像引导,论文

General Image-to-Image Translation with One-Shot Image Guidance 论文:https://arxiv.org/abs/2307.14352 代码:https://github.com/crystalneuro/visual-concept-translator 文章目录 Abstract1. Introduction2. 相关工作2.1 图像到图像转换2.2. Di…

一键登录和短信验证登录,到底有什么区别?

一键登录是什么? 本机号码一键登录验证是一种登录认证方式,通过获取用户手机上的本机号码来验证用户身份,从而实现快捷登录和简化登录流程的目的。 在使用一键登录时,首先需要用户在登录页面选择使用本机号码一键登录&#xff0…

ROS学习笔记之——路径规划及avoid obstacles

之前博客《ROS学习笔记之——Navigation Stack及路径规划》介绍了navigation stack,其中涉及到的amcl、路径规划以及避障还没有详细的展开 目录 AMCL 路径规划 全局路径规划中的地图 栅格地图(Grid Map) 概率图(Cost Map) 特征地图(Feature Map) 拓扑地图(Topo…

排序-堆排序

给你一个整数数组 nums,请你将该数组升序排列。 输入:nums [5,2,3,1] 输出:[1,2,3,5] 输入:nums [5,1,1,2,0,0] 输出:[0,0,1,1,2,5] 思路直接看我录制的视频吧 算法-堆排序_哔哩哔哩_bilibili 实现代码如下所示&…

网络防御(2)

1. 什么是防火墙? 2. 状态防火墙工作原理? 3. 防火墙如何处理双通道协议? 一、什么是防火墙? 防火墙是一种网络安全设备或软件,用于保护计算机网络免受未经授权的访问,并管理网络流量。它作为一个安全边界…

Android中级——RemoteView

RemoteView RemoteView的应用NotificationWidgetPendingIntent RemoteViews内部机制模拟RemoteViews RemoteView的应用 Notification 如下开启一个系统的通知栏,点击后跳转到某网页 public class MainActivity extends AppCompatActivity {private static final …

【Linux取经路】进程的奥秘

文章目录 1、什么是进程?1.1 自己写一个进程 2、操作系统如何管理进程?2.1 描述进程-PCB2.2 组织进程2.3 深入理解进程 3、Linux环境下的进程3.1 task_struct3.2 task_struct内容分类3.3 组织进程3.4 查看进程属性 4、结语 1、什么是进程? 在…

软件单元测试

单元测试目的和意义 对于非正式的软件(其特点是功能比较少,后续也不有新特性加入,不用负责维护),我们可以使用debug单步执行,内存修改,检查对应的观测点是否符合要求来进行单元测试&#xff0c…