参数校验 Lua API

概述

lvalidate 是一个 Lua 参数校验库,提供参数的类型和范围验证功能。

模块加载

local lvalidate = require('lvalidate')

主要特性

  • 整数校验:check_integer 验证整数类型及范围,区分整值与浮点数
  • 字符串长度校验:lens 和 len_or_none 检查字符串长度边界
  • 数值范围校验:ranges 和 range_or_none 验证数值(含浮点数)范围
  • 正则匹配:regex 和 regex_or_none 使用标准正则表达式校验字符串格式
  • JSON 合法性:Json 快速验证字符串是否为合法 JSON
  • 可选的 nil 穿透:_or_none 变体允许 nil 值直接通过,简化可选参数校验
  • 错误消息格式化:need_convert 参数控制错误消息格式,适配不同日志输出场景

函数

check_integer

验证一个值是否为整数类型,并且是否在指定的范围内。

语法

lvalidate.check_integer(name, val, min, max, need_convert)

参数

  • name (string) - 属性名称,用于错误消息中标识属性
  • val (number) - 要验证的值
  • min (number) - 允许的最小值(包含)
  • max (number) - 允许的最大值(包含)
  • need_convert (boolean) - 是否在错误消息中使用转换格式
    • true - 错误消息中的属性名和值会添加 % 前缀(如 %Prop1 和 %Prop1:150)
    • false - 使用原始格式(如 Prop1 和 150)

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

异常

函数在以下情况下会抛出 Lua 错误:

  1. 类型错误 (PropertyValueTypeError):

    • val 不是数字类型
    • val 是浮点数但有小数部分(如 3.14、5.5)
    • val 是 NaN(Not a Number)
  2. 范围错误 (PropertyValueOutOfRange):

    • val 小于 min 或大于 max
    • min 或 max 不是数字类型

验证规则

  1. 类型检查:

    • val 必须是数字类型(LUA_TNUMBER)
    • 如果 val 是浮点数,必须没有小数部分(即必须是整数值)
    • 例如:42 ✓、42.0 ✓、3.14 ✗、5.5 ✗
  2. 范围检查:

    • val 必须在 [min, max] 范围内(包含边界)
    • min 和 max 必须是数字类型
    • 例如:val=50, min=0, max=100 ✓、val=150, min=0, max=100 ✗
  3. 边界值:

    • 边界值(min 和 max)是包含的
    • 例如:val=0, min=0, max=100 ✓、val=100, min=0, max=100 ✓

错误消息格式

类型错误:

PropertyValueTypeError: The value '<val>' for the property <name> is of a different type than the property can accept.

范围错误:

PropertyValueOutOfRange: The value '<val>' for the property <name> is not in the supported range of acceptable values.

当 need_convert 为 true 时:

  • 属性名格式:%<name>(如 %Prop1)
  • 值格式:%<name>:<val>(如 %Prop1:150)

示例

基本用法
local lvalidate = require('lvalidate')

-- 验证有效整数
lvalidate.check_integer("Prop1", 42, 0, 100, false)
-- 成功,无返回值

-- 验证边界值
lvalidate.check_integer("Prop1", 0, 0, 100, false)   -- 最小值边界
lvalidate.check_integer("Prop1", 100, 0, 100, false) -- 最大值边界
错误处理
local lvalidate = require('lvalidate')

-- 捕获类型错误
local success, err = pcall(lvalidate.check_integer, "Prop1", 3.14, 0, 100, false)
if not success then
    print("类型错误:", err)
    -- 输出: PropertyValueTypeError: The value '3.14' for the property Prop1 ...
end

-- 捕获范围错误
local success, err = pcall(lvalidate.check_integer, "Prop1", 150, 0, 100, false)
if not success then
    print("范围错误:", err)
    -- 输出: PropertyValueOutOfRange: The value '150' for the property Prop1 ...
end
使用 need_convert
local lvalidate = require('lvalidate')

-- need_convert = false(默认格式)
local success, err = pcall(lvalidate.check_integer, "Prop1", 150, 0, 100, false)
-- 错误消息: PropertyValueOutOfRange: The value '150' for the property Prop1 ...

-- need_convert = true(转换格式)
local success, err = pcall(lvalidate.check_integer, "Prop1", 150, 0, 100, true)
-- 错误消息: PropertyValueOutOfRange: The value '%Prop1:150' for the property %Prop1 ...
负数和跨零范围
local lvalidate = require('lvalidate')

-- 负数范围
lvalidate.check_integer("Temperature", -5, -10, 0, false)  -- ✓ 通过

-- 跨零范围
lvalidate.check_integer("Offset", 0, -10, 10, false)     -- ✓ 通过
lvalidate.check_integer("Offset", -5, -10, 10, false)     -- ✓ 通过
lvalidate.check_integer("Offset", 5, -10, 10, false)      -- ✓ 通过
相等边界
local lvalidate = require('lvalidate')

-- min == max == val
lvalidate.check_integer("FixedValue", 5, 5, 5, false)  -- ✓ 通过

lens

验证字符串长度是否在指定的范围内。

语法

lvalidate.lens(name, val, min, max, need_convert)

参数

  • name (string) - 属性名称,用于错误消息中标识属性
  • val (string) - 要验证的字符串
  • min (number) - 允许的最小长度(包含)
  • max (number) - 允许的最大长度(包含)
  • need_convert (boolean) - 是否在错误消息中使用转换格式
    • true - 错误消息中的属性名和值会添加 % 前缀(如 %Prop1 和 %Prop1:abc)
    • false - 使用原始格式(如 Prop1 和 abc)

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

异常

函数在以下情况下会抛出 Lua 错误:

  1. 类型错误 (PropertyValueTypeError):

    • val 不是字符串类型
  2. 长度小于下限 (StringValueTooShort):

    • val 长度小于 min
  3. 长度大于上限 (StringValueTooLong):

    • val 长度大于 max

len_or_none

验证字符串长度是否在指定的范围内,允许入参值为 nil。

语法

lvalidate.len_or_none(name, val, min, max, need_convert)

参数

  • name (string) - 属性名称,用于错误消息中标识属性
  • val (string | nil) - 要验证的字符串;为 nil 时跳过检查
  • min (number) - 允许的最小长度(包含)
  • max (number) - 允许的最大长度(包含)
  • need_convert (boolean) - 是否在错误消息中使用转换格式
    • true - 错误消息中的属性名和值会添加 % 前缀(如 %Prop1 和 %Prop1:abc)
    • false - 使用原始格式(如 Prop1 和 abc)

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

行为说明

  • 当 val == nil 时,直接返回,不抛出错误
  • 当 val ~= nil 时,行为与 lens 相同

异常

函数在以下情况下会抛出 Lua 错误:

  1. 类型错误 (PropertyValueTypeError):

    • val 不是nil,并且不是字符串类型
  2. 长度小于下限 (StringValueTooShort):

    • val 长度小于 min
  3. 长度大于上限 (StringValueTooLong):

    • val 长度大于 max

ranges

验证一个数值是否在指定的范围内(允许浮点数)。

语法

lvalidate.ranges(name, val, min, max, need_convert)

参数

  • name (string) - 属性名称,用于错误消息中标识属性
  • val (number) - 要验证的值(整数或浮点数)
  • min (number) - 允许的最小值(包含)
  • max (number) - 允许的最大值(包含)
  • need_convert (boolean) - 是否在错误消息中使用转换格式
    • true - 错误消息中的属性名和值会添加 % 前缀(如 %Prop1 和 %Prop1:1.23)
    • false - 使用原始格式(如 Prop1 和 1.23)

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

行为说明

  • val 必须是 Lua 数字类型
  • val 必须在 [min, max] 范围内(包含边界)
  • min 和 max 必须是数字类型,否则会抛出错误

异常

函数在以下情况下会抛出 Lua 错误:

  1. 类型错误 (PropertyValueTypeError):

    • val 不是数字类型
  2. 范围错误 (PropertyValueOutOfRange):

    • val 小于 min 或大于 max

range_or_none

验证一个数值是否在指定的范围内,允许入参为 nil(此时不做范围检查)。

语法

lvalidate.range_or_none(name, val, min, max, need_convert)

参数

  • name (string) - 属性名称,用于错误消息中标识属性
  • val (number | nil) - 要验证的值;为 nil 时跳过检查
  • min (number) - 允许的最小值(包含)
  • max (number) - 允许的最大值(包含)
  • need_convert (boolean) - 是否在错误消息中使用转换格式

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

行为说明

  • 当 val == nil 时,直接返回,不抛出错误
  • 当 val ~= nil 时,行为同 ranges

异常

函数在以下情况下会抛出 Lua 错误:

  1. 类型错误 (PropertyValueTypeError):

    • val 不是nil,并且不是数字类型
  2. 范围错误 (PropertyValueOutOfRange):

    • val 不是nil,并且小于 min 或大于 max

regex

验证字符串是否匹配指定的标准正则表达式。

语法

lvalidate.regex(name, val, pattern, need_convert)

参数

  • name (string) - 属性名称,用于错误消息中标识属性
  • val (string) - 要验证的字符串
  • pattern (string) - 标准正则表达式
  • need_convert (boolean) - 是否在错误消息中使用转换格式

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

行为说明

  • 当 val 不是字符串时,抛出类型相关错误
  • 当 val 不匹配给定的 pattern 时,抛出匹配失败的错误

异常

函数在以下情况下会抛出 Lua 错误:

  1. 类型错误 (PropertyValueTypeError):

    • val 不是字符串类型
  2. 正则不匹配 (PropertyValueFormatError):

    • val 不匹配给定的 pattern

regex_or_none

验证字符串是否匹配指定的标准正则表达式,允许入参为 nil。

语法

lvalidate.regex_or_none(name, val, pattern, need_convert)

参数

  • name (string) - 属性名称,用于错误消息中标识属性
  • val (string | nil) - 要验证的字符串;为 nil 时跳过检查
  • pattern (string) - 标准正则表达式
  • need_convert (boolean) - 是否在错误消息中使用转换格式

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

行为说明

  • 当 val == nil 时,直接返回,不抛出错误
  • 当 val ~= nil 时,行为同 regex

异常

函数在以下情况下会抛出 Lua 错误:

  1. 类型错误 (PropertyValueTypeError):

    • val 不是nil,并且不是字符串类型
  2. 正则不匹配 (PropertyValueFormatError):

    • val 不是nil,并且不匹配给定的 pattern

Json

验证一个字符串是否是合法的 JSON 文本。

语法

lvalidate.Json(val)

参数

  • val (string) - 要验证的字符串

返回值

  • 无返回值。如果验证通过,函数正常返回;如果验证失败,会抛出 Lua 错误。

行为说明

  • 当 val 不是字符串时,抛出 MalformedJSON 错误
  • 使用 JSON 解析库对 val 进行反序列化
  • 如果反序列化失败(不是合法的 JSON),抛出 MalformedJSON 错误

异常

函数在以下情况下会抛出 Lua 错误:

  1. 不合法JSON (MalformedJSON):
    • val 不是字符串类型,或
    • val 不是合法的 JSON 字符串

最佳实践

✅ 用 pcall 统一捕获所有校验错误

lvalidate 函数在失败时抛出 Lua 错误,统一使用 pcall 包装,避免校验失败中断业务流程:

-- ✅ 统一捕获校验错误
local ok, err = pcall(lvalidate.check_integer, "Port", port, 1, 65535, false)
if not ok then
    log:error("参数校验失败: ${err}", "err", tostring(err))
    return nil, err
end

✅ 可选参数使用 _or_none 变体

当参数允许为空时,直接使用 _or_none 函数,避免手写 nil 判断:

-- ✅ 用 len_or_none 处理可选字符串
lvalidate.len_or_none("Description", desc, 0, 256, false)
-- desc 为 nil 时自动跳过,无需额外 if

❌ 不要在校验循环中逐条 catch 来收集错误

-- ❌ 逐条捕获效率低且信息不完整
for _, rule in ipairs(rules) do
    local ok = pcall(lvalidate.check_integer, rule.name, val, rule.min, rule.max, false)
    if not ok then return false end
end

-- ✅ 批量校验,先收集再报错
local errors = {}
for _, rule in ipairs(rules) do
    local ok, err = pcall(lvalidate.check_integer, rule.name, val, rule.min, rule.max, false)
    if not ok then table.insert(errors, tostring(err)) end
end
if #errors > 0 then
    log:error("批量校验失败: ${count} 项", "count", #errors)
end

❌ 避免对非 JSON 字符串使用 Json 函数

Json 内部会进行完整的 JSON 反序列化,开销较大。仅在确实需要验证 JSON 时才使用:

-- ❌ 对普通字符串不该用 Json
lvalidate.Json("hello world")  -- 抛异常且无必要

-- ✅ Json 仅用于需要确认 JSON 格式的场景
lvalidate.Json('{"key":"value"}')