将请求主体与整个请求分开验证

Validate request body separately from request as a whole

我有一个关于验证 PUT 请求的问题。请求的主体是一个对象数组。如果主体包含长度至少为 1 的数组,我希望请求成功,但我还需要对数组中的每个对象进行单独的验证,并将其传回响应中。所以我的 put body 是:

[1, 2, {id: "thirdObject"}]

即使前两项甚至不是对象,响应也应为 200。如果在正文中传递长度为 1 的数组,则请求只需要成功。响应需要类似于:

[{id: firstObject, status: 400, error: should be object}, {id: secondObject, status: 400, error: should be object}, { id: thirdObject, status: 204 }]

目前我正在使用流畅的模式验证正文:

body: S.array().items(myObjectSchema) .minItems(1)

如果正文中的任何项目与 myObjectSchema 不匹配,这将导致 400。想知道您是否知道如何实现这一目标?

我不知道您使用的架构语法,但使用 JSON 架构的草案 7(https://json-schema.org/specification-links.html, and see also https://json-schema.org/understanding-json-schema 供参考 material),您可以:

{
  "type": "array",
  "minItems": 1
}

如果您想确保至少一个,但不一定所有项与您的对象类型匹配,则添加"contains" 关键词:

{
  ...,
  "contains": ... reference to your object schema here
}

验证不会告诉您架构是否成功(例如{ id: thirdObject, status: 204 }),因此您需要自行管理它。

为此,您需要创建一个错误处理程序来读取验证错误并与请求正文合并:

const fastify = require('fastify')()
const S = require('fluent-schema')

fastify.put('/', {
  handler: () => { /** this will never executed if the schema validation fail */ },
  schema: {
    body: S.array().items(S.object()).minItems(1)
  }
})

const errorHandler = (error, request, reply) => {
  const { validation, validationContext } = error

  // check if we have a validation error
  if (validation) {
    // here the validation error
    console.log(validation)

    // here the body
    console.log(request.body)

    reply.send(validation)
  } else {
    reply.send(error)
  }
}

fastify.setErrorHandler(errorHandler)

fastify.inject({
  method: 'PUT',
  url: '/',
  payload: [1, 2, { id: 'thirdObject' }]
}, (_, res) => {
  console.log(res.json())
})

这将记录:

[
  {
    keyword: 'type',
    dataPath: '[0]',
    schemaPath: '#/items/type',
    params: { type: 'object' },
    message: 'should be object'
  },
  {
    keyword: 'type',
    dataPath: '[1]',
    schemaPath: '#/items/type',
    params: { type: 'object' },
    message: 'should be object'
  }
]

[ 1, 2, { id: 'thirdObject' } ]

如您所见,感谢 validation[].dataPath,您能够了解正文数组的哪些元素无效并将数据合并到 return 您的信息。

考虑到在这种情况下不会执行处理程序。如果无论验证如何都需要执行它,则应该在 preHandler 挂钩中执行验证作业并避免默认模式验证检查(因为它是阻塞的)


编辑

const fastify = require('fastify')()
const S = require('fluent-schema')

let bodyValidator
fastify.decorateRequest('hasError', function () {
  if (!bodyValidator) {
    bodyValidator = fastify.schemaCompiler(S.array().items(S.object()).minItems(1).valueOf())
  }

  const valid = bodyValidator(this.body)
  if (!valid) {
    return bodyValidator.errors
  }
  return true
})

fastify.addHook('preHandler', (request, reply, done) => {
  const errors = request.hasError()
  if (errors) {
    console.log(errors)
    // show the same errors as before
    // you can merge here or set request.errors = errors to let the handler read them
    reply.send('here merge errors and request.body')
    return
  }

  done() // needed to continue if you don't reply.send
})

fastify.put('/', { schema: { body: S.array() } }, (req, reply) => {
  console.log('handler')
  reply.send('handler')
})

fastify.inject({
  method: 'PUT',
  url: '/',
  payload: [1, 2, { id: 'thirdObject' }]
}, (_, res) => {
  console.log(res.json())
})