Adonis JS 5:如何让验证器类同时支持HTTP请求与普通数据验证?
让Adonis的TaskValidator同时支持HTTP请求和无上下文数据验证
我在Adonis中编写了TaskValidator验证器类,它能正常处理控制器中的常规HTTP请求验证。但我希望它也能在无HTTP请求(即无ctx上下文)的场景下验证普通数据,且不想为此单独创建另一个验证器。
现有代码问题
当前验证器硬依赖HttpContextContract,schema中直接从this.ctx.request.body()和this.ctx.params取数据,导致无法在无ctx的场景下复用。
原验证器代码
import { schema, rules, CustomMessages } from '@ioc:Adonis/Core/Validator' import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' export default class TaskValidator { constructor(protected ctx: HttpContextContract) {} public schema = schema.create({ name: schema.string({ trim: true }, [ rules.maxLength(255), rules.unique({ table: 'tasks', column: 'name', whereNot: { id: this.ctx.params?.id ?? 0 }, where: { matter_id: this.ctx.request.body().matter_id?? -1, deadline: this.ctx.request.body().deadline?? -1, }, }), ]), // 其他字段... }) public messages: CustomMessages = { // 自定义错误消息... } }
无上下文场景的使用尝试
public static async createTask( event: Event, matter: Matter, rule: Rule, taskDeadline: DateTime, workflow: Workflow, workflowDeadline: DateTime, workflowStep: Workflowstep ) { const task = new Task() task.name = workflowStep.name task.deadline = taskDeadline task.workflowDeadline = workflowDeadline task.workflowstepId = workflowStep.id task.sequence = workflowStep.sequence task.ruleId = rule.id task.workflowId = workflow.id task.eventId = event.id task.matterId = matter.id task.isActive = true task.status = 'open' task.actorId = null // 尝试伪造ctx但无法正常工作 const taskValidator = new TaskValidator({} as HttpContextContract) try { await validator.validate({ schema: taskValidator.schema, data: task, messages: taskValidator.messages, }) await Task.create(task) } catch (error) { console.error(error.messages) } }
解决方案
1. 修改验证器构造函数,支持双模式
让构造函数同时接受HttpContextContract或普通数据对象,内部统一处理数据来源和参数:
import { schema, rules, CustomMessages } from '@ioc:Adonis/Core/Validator' import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' export default class TaskValidator { protected data: Record<string, any> protected paramsId: number // 构造函数重载:要么传ctx,要么传数据+可选id constructor(input: HttpContextContract | Record<string, any>, paramsId?: number) { if ('request' in input) { // HTTP请求场景:从ctx中提取数据和参数 this.data = input.request.body() this.paramsId = input.params?.id ?? 0 } else { // 无上下文场景:直接使用传入的数据和id this.data = input this.paramsId = paramsId ?? 0 } } public schema = schema.create({ name: schema.string({ trim: true }, [ rules.maxLength(255), rules.unique({ table: 'tasks', column: 'name', whereNot: { id: this.paramsId }, where: { matter_id: this.data.matter_id ?? -1, deadline: this.data.deadline ?? -1, }, }), ]), // 其他字段的schema定义保持不变 }) public messages: CustomMessages = { 'name.unique': '该任务名称已存在', 'name.maxLength': '任务名称长度不能超过255字符', // 其他自定义错误消息 } }
2. 无上下文场景的正确使用
直接传入待验证的task对象即可,无需伪造ctx:
public static async createTask( event: Event, matter: Matter, rule: Rule, taskDeadline: DateTime, workflow: Workflow, workflowDeadline: DateTime, workflowStep: Workflowstep ) { const task = new Task() task.name = workflowStep.name task.deadline = taskDeadline task.workflowDeadline = workflowDeadline task.workflowstepId = workflowStep.id task.sequence = workflowStep.sequence task.ruleId = rule.id task.workflowId = workflow.id task.eventId = event.id task.matterId = matter.id task.isActive = true task.status = 'open' task.actorId = null // 直接传入task对象初始化验证器 const taskValidator = new TaskValidator(task) try { await validator.validate({ schema: taskValidator.schema, data: task, messages: taskValidator.messages, }) await Task.create(task) } catch (error) { console.error(error.messages) } }
3. 原有HTTP请求场景兼容
控制器中原来的使用方式完全不需要修改,依然可以正常工作:
export default class TasksController { public async update(ctx: HttpContextContract) { const validator = new TaskValidator(ctx) const validatedData = await ctx.request.validate({ schema: validator.schema, messages: validator.messages }) // 后续业务逻辑... } }
内容的提问来源于stack exchange,提问作者Ash
相关产品推荐
相关产品推荐

