You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Joi中custom()与external()方法的区别及适用场景

Joi中custom()与external()的核心区别及适用场景

虽然你用两种方法都实现了字符串trim的效果,但它们在设计意图、执行时机和适用场景上有本质区别:

1. 执行时机与验证流程位置

  • custom():属于字段级的内置验证扩展,在当前字段的所有内置规则(比如string()、required())执行完成后立即触发,是Joi核心验证流程的一部分。它可以直接修改字段值并返回,后续的规则(如果有的话)会基于修改后的值继续验证。
  • external():属于整个Schema验证完成后的“外部钩子”,会等所有字段的内置规则、custom规则都执行完毕,整个数据结构验证通过后才运行。它更像是对最终验证结果的二次校验或补充。

2. 核心用途与场景

  • custom() 适合:

    • 字段级的同步转换(比如你的trim示例、格式化手机号)
    • 轻量的同步自定义验证(比如校验字符串是否符合特定格式)
      示例:
    const schema = Joi.object({
      phone: Joi.string().custom((value, helpers) => {
        // 清除非数字字符
        const cleanedPhone = value.replace(/\D/g, '');
        if (cleanedPhone.length !== 11) {
          return helpers.error('string.invalidPhone');
        }
        return cleanedPhone; // 返回修改后的值
      }).required()
    });
    
  • external() 适合:

    • 需要异步操作的验证(比如查询数据库检查邮箱是否已注册)
    • 依赖整个Schema最终验证结果的校验(比如验证两个字段的组合是否符合业务规则)
      示例:
    const schema = Joi.object({
      email: Joi.string().email().external(async (value, helpers) => {
        // 异步查询数据库
        const userExists = await UserModel.findOne({ email: value });
        if (userExists) {
          return helpers.error('any.emailExists');
        }
        return value;
      }).required()
    });
    

3. 异步支持的设计差异

虽然custom()也能返回Promise实现异步,但Joi的设计初衷是让external()专门处理异步场景——它的API更适配异步逻辑,且在错误处理、流程优先级上更适合这类场景。而custom()更偏向同步的字段级处理,用它做异步会显得不符合设计意图。

简单总结:如果是字段内部的同步转换/轻量验证,用custom();如果是需要异步、依赖全局上下文或整个Schema结果的验证,用external()。

内容的提问来源于stack exchange,提问作者Ashutosh Aswal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 23:22:18