---
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以内;输出为规范化数据并与源输入隔离。

发表评论 取消回复