---

title: API输入规范与全链路参数净化(统一DSL与校验库)最佳实践

keywords:

  • 输入校验
  • 参数净化
  • DSL
  • Schema
  • 枚举
  • 长度限制
  • 规范化
  • 白名单
  • ReDoS
  • 中间件

description: 建立统一的参数DSL与校验库,实现前后端一致的类型、范围、长度、枚举与规范化约束,并提供可验证的路由中间件与示例,降低ReDoS与污染风险。

categories:

  • 文章资讯
  • 技术教程

---

一、背景与目标

  • 风险:类型不一致、超长与非法字符、正则灾难回溯、枚举越权、跨层污染。
  • 目标:统一DSL定义、可编译校验器、输入净化与规范化、路由中间件全链路执行。

二、参数DSL与定义

type StrSpec = { type: 'string'; min: number; max: number; pattern?: RegExp; trim?: boolean; lower?: boolean }
type NumSpec = { type: 'number'; min: number; max: number; integer?: boolean }
type BoolSpec = { type: 'boolean' }
type EnumSpec = { type: 'enum'; values: string[] }
type FieldSpec = StrSpec | NumSpec | BoolSpec | EnumSpec
type Schema = { properties: Record<string, FieldSpec>; required?: string[] }

三、正则与长度约束(含超时防护)

function regexTestWithTimeout(re: RegExp, input: string, ms: number): boolean {
  const start = Date.now()
  const ok = re.test(input)
  return ok && Date.now() - start < ms
}

四、净化与规范化

function canonicalizeString(input: string, spec: StrSpec): string {
  let s = input
  if (spec.trim) s = s.trim()
  if (spec.lower) s = s.toLowerCase()
  return s
}

function parseNumber(input: any, spec: NumSpec): number | null {
  const n = typeof input === 'number' ? input : Number(input)
  if (!Number.isFinite(n)) return null
  if (spec.integer && !Number.isInteger(n)) return null
  if (n < spec.min || n > spec.max) return null
  return n
}

五、编译校验器

type Result = { ok: boolean; data?: Record<string, any> }

function compile(schema: Schema) {
  return function validate(input: Record<string, any>): Result {
    const out: Record<string, any> = {}
    for (const [key, spec] of Object.entries(schema.properties)) {
      const v = input[key]
      if (schema.required?.includes(key) && v === undefined) return { ok: false }
      if (v === undefined) continue
      if (spec.type === 'string') {
        const s = canonicalizeString(String(v), spec)
        if (s.length < spec.min || s.length > spec.max) return { ok: false }
        if (spec.pattern && !regexTestWithTimeout(spec.pattern, s, 10)) return { ok: false }
        out[key] = s
      } else if (spec.type === 'number') {
        const n = parseNumber(v, spec)
        if (n === null) return { ok: false }
        out[key] = n
      } else if (spec.type === 'boolean') {
        const b = typeof v === 'boolean' ? v : String(v).toLowerCase() === 'true'
        out[key] = b
      } else if (spec.type === 'enum') {
        const s = String(v)
        if (!spec.values.includes(s)) return { ok: false }
        out[key] = s
      }
    }
    return { ok: true, data: out }
  }
}

六、路由中间件整合

type Req = { query: Record<string, any>; body: any }
type Res = { status: (n: number) => Res; end: (b?: string) => void }

function guard(schema: Schema) {
  const validate = compile(schema)
  return function handler(req: Req, res: Res, next: Function) {
    const merged = { ...req.query, ...req.body }
    const r = validate(merged)
    if (!r.ok) return res.status(400).end('invalid_input')
    ;(req as any).validated = r.data
    next()
  }
}

七、示例与验收

const userCreate: Schema = {
  properties: {
    username: { type: 'string', min: 3, max: 32, pattern: /^[a-z0-9_\-]+$/i, trim: true, lower: false },
    email: { type: 'string', min: 6, max: 128, pattern: /^\S+@\S+\.\S+$/, trim: true, lower: true },
    age: { type: 'number', min: 0, max: 120, integer: true },
    role: { type: 'enum', values: ['user','admin'] }
  },
  required: ['username','email']
}

function createUser(req: Req, res: Res) {
  const data = (req as any).validated
  res.end(JSON.stringify({ ok: true, data }))
}
  • 验收项:类型、长度、枚举、范围严格通过;正则测试耗时在10ms以内;输出为规范化数据并与源输入隔离。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部