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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 06:41:13