【Express.js】使用zod检验

news/2024/7/19 11:01:11 标签: express, javascript, 后端, 教程

使用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.niftyadmin.cn/n/4925817.html

相关文章

Kafka3.0.0版本——Broker(服役新节点)示例

目录 一、服务器信息二、VMware克隆服务节点(将虚拟机1克隆成虚拟机4 )三、克隆后的(192.168.136.30)服务节点配置文件修改3.1、修改zookeeper服务配置文件3.2、修改kafka服务配置文件3.3、先启动zookeeper,再启动kafk…

OpenCV-SIFT算法详解

系列文章目录 文章目录 系列文章目录引言一、高斯金字塔二、高斯差分金字塔三、特征点处理四、特征点描述子总结 引言 SIFT算法是为了解决图片的匹配问题,想要从图像中提取一种对图像的大小和旋转变化保持鲁棒的特征,从而实现匹配。这一算法的灵感也十分…

2023河南萌新联赛第(五)场:郑州轻工业大学 --01分数规划

题目描述 给定一个字符串 s,仅含 0, 1, ? 三种字符,你必须将所有 ? 替换为 1 或 0 。 定义 s 的美好值为将所有?进行替换后,s的最长连续 1 或 0 的子串的长度。请你进行所有替换后,使得字符串 s 的美好值最大,请输…

深入理解Maven中的properties标签

系列文章目录 文章目录 系列文章目录前言一、properties标签的基本语法二、使用properties标签配置项目版本三、使用properties标签配置依赖版本四、使用properties标签配置插件版本五、使用properties标签实现条件判断六、使用外部properties文件总结前言 在Maven项目中,pro…

React源码解析18(2)------ FilberNode,FilberRootNode结构关系

摘要 在上一篇,我们实现了通过JSX转换为ReactElement的方法,也看到了转换后React元素的结构。但是这个React元素,并不能很清楚的表达组件之间的关系,以及属性的处理。 所以在React内部,会将所有的React元素转换为Fil…

【二分】CF1623 C

Problem - 1623C - Codeforces 题意: 思路: 肯定是二分,我们去二分最小值,然后check的时候最小值要大于mid check的时候要让最小值尽可能大 注意到我们不需要去管最大值,只需要最小值尽可能大就好了,因…

分布式搜索ElasticSearch-ES(一)

一、ElasticSearch介绍 ES是一款非常强大的开源搜索引擎,可以帮我们从海量的数据中快速找到我们需要的内容。 ElasticSearch结合kibana、Logstash、Beats,也就是elastic stack(ELK),被广泛运用在日志数据分析,实时监控等领域。 …

uniapp 实现滑动视图切换 顶部滚动导航栏

无论小程序的时候一般有这个功能,在页面处于首页时候,滑动视图,切换视图顶部滚动导航也跟着切换 1.想要实现这个功能就需要实现顶部导航栏,首先实现顶部滚导航栏 点击高亮颜色显示 模板代码 <scroll-view scroll-x"true" class"scroll-content" > …