如何使用Joi验证对象数组?

76

我正在将一个包含服务名称的对象数组发送到后端,其结构如下所示

[{"serviceName":"service1"},
{"serviceName":"service2"},..]

当我在后端获取数组时,我希望验证数组中的每个对象是否具有serviceName属性。

我已经编写了以下代码,但即使我传递有效的数组,我仍然会收到验证错误。

var Joi = require('joi');
var service = Joi.object().keys({
  serviceName: Joi.string().required()
});

var services = Joi.array().ordered(service);

var test = Joi.validate([{serviceName:'service1'},{serviceName:'service2'}],services)

对于上面的代码,我总是得到一个带有错误信息的验证错误。

"value" at position 1 fails because array must contain at most 1 items
6个回答

143

items替换ordered将起作用。

let Joi = require('joi')
let service = Joi.object().keys({
  serviceName: Joi.string().required(),
})

let services = Joi.array().items(service)

let test = Joi.validate(
  [{ serviceName: 'service1' }, { serviceName: 'service2' }],
  services,
)

请点击此处查看参考文献


正是我所需要的。谢谢! - Yuschick
6
Joi.validate自版本16起已被弃用。为了保持相同的解决方案,现在您只需使用services.validate([], callback)services.validateAsync([])来替代Joi.validate([], services) - Jeffery ThaGintoki
2
它将通过空数组service = []的验证,使用Joi.array().min(1).items(service)确保service包含一些值。 - sujeet
事实上,现在也不需要使用.key()函数,这个代码也可以正常工作。让我们定义一个服务对象:let service = Joi.object({ serviceName: Joi.string().required(), }); - Ahmer Saeed

30

一个基本/更清晰的例子如下所示。 要验证像这样的 JSON 请求:

   {
    "data": [
            {
        "keyword":"test",
        "country_code":"de",
        "language":"de",
        "depth":1
            }
        ]
    }

这里是 Joi 验证:

 seoPostBody: {
    body: {
      data: Joi.array()
        .items({
          keyword: Joi.string()
            .required(),
          country_code: Joi.string()
            .required(),
          language: Joi.string()
            .required(),
          depth: Joi.number()
            .required(),
        }),
    },
  };

这是我在NodeJs中正在做的事情,可能需要针对其他平台进行一些小改变。


谢谢,你提供的示例对我的用例有所帮助;json"tags": [ "61b394ead5a3863d6a41fedf", "61b292f38561f0c5550ca78c" ]验证tags: Joi.array().max(4).min(1).items(Joi.string()) - Cuado

8
const test = {
body: Joi.array()
    .items({
        x: Joi.string().required(),
        y: Joi.string().required(),
        z: Joi.string().required(),
        date: Joi.string().required(),
    })
};

3
欢迎来到 Stack Overflow。在 Stack Overflow 上,不鼓励仅包含代码的答案,因为它们没有说明如何解决问题。请编辑你的答案,说明这段代码的作用以及如何改进已有的顶赞答案,使其对其他遇到类似问题的用户有用。 - FluffyKitten

3

我希望让它更加清晰。我目前使用的是"@hapi/joi:16.1.7"。

假设您想让模式验证此对象数组。

const example = [
   {
      "foo": "bar",
      "num": 1,
      "is_active": true,
   }
];

然后模式的规则应该是:
var validator = require('@hapi/joi');

const rules = validator.array().items(
    validator.object(
        foo: validator.string().required(),
        num: validator.number().required(),
        is_active: validator.boolean().required(),
    ),
);

const { error } = rules.validate(example);

2

对于 Joi,您可以使用以下代码,这对我非常有效。它将验证数组必须至少有一个带有键名“serviceName”的对象。

const Joi = require('joi');
const itemsArray = Joi.array().items(
            Joi.object({
                serviceName: Joi.string().required(),
            })
        ).min(1).required();

        const itemSchema = Joi.array().items(itemsArray).when('checkout_type', {
            is: 'guest',
            then: Joi.array().required(),
        }).required();

let schema = Joi.object().keys({
        items: Joi.alternatives().try(itemsArray, itemSchema).required(),
    });

如果我想匹配其中一个模式,如果匹配对象则忽略另一个模式怎么办?https://dev59.com/w8Xsa4cB1Zd3GeqPk47D - Bravo

0

这样的库非常棒,但如果我们能以更无缝的方式使用它们,比如在请求管道中使用,那不是更好吗?首先让我们看看如何在Node.js的Express应用程序中使用Joi:

const Joi = require('joi'); 
app.post('/blog', async (req, res, next) => { 
  const { body } = req; const 
  blogSchema = Joi.object().keys({ 
    title: Joi.string().required 
    description: Joi.string().required(), 
    authorId: Joi.number().required() 
  }); 
  const result = Joi.validate(body, blogShema); 
  const { value, error } = result; 
  const valid = error == null; 
  if (!valid) { 
    res.status(422).json({ 
      message: 'Invalid request', 
      data: body 
    }) 
  } else { 
    const createdPost = await api.createPost(data); 
    res.json({ message: 'Resource created', data: createdPost }) 
  } 
});

以上方法可行。但是我们需要为每个路由执行以下操作:

  1. 创建模式
  2. 调用 validate()

这种方法有点不够优雅。我们想要一些看起来更加流畅的东西。

构建中间件

让我们尝试将其重构为一个中间件。在 Express 中,中间件只是我们需要时可以插入请求管道的东西。在我们的情况下,我们希望尝试验证我们的请求并尽早确定是否值得继续进行或中止它。

所以让我们看看中间件。它只是一个函数,对吧:

const handler = (req, res, next) = { // handle our request } 
const middleware = (req, res, next) => { // to be defined } 
app.post( '/blog', middleware, handler )

如果我们能够向中间件提供模式,那将会很方便,因为在中间件函数中我们只需要做类似于这样的事情:

(req, res, next) => { 
  const result = Joi.validate(schema, data) 
}

我们可以创建一个带有工厂函数和模块的模块,用于管理所有的模式。让我们先来看看我们的工厂函数模块:

const Joi = require('joi'); 
const middleware = (schema, property) => { 
  return (req, res, next) => { 
  const { error } = Joi.validate(req.body, schema); 
  const valid = error == null; 
  
  if (valid) { 
    next(); 
  } else { 
    const { details } = error; 
    const message = details.map(i => i.message).join(',');
 
    console.log("error", message); 
   res.status(422).json({ error: message }) } 
  } 
} 
module.exports = middleware;

接下来,让我们为所有模式创建一个模块,就像这样:

// schemas.js 
const Joi = require('joi') 
const schemas = { 
  blogPOST: Joi.object().keys({ 
    title: Joi.string().required 
    description: Joi.string().required() 
  }) 
  // define all the other schemas below 
}; 
module.exports = schemas;

好的,那么让我们回到我们的应用程序文件:

// app.js 
const express = require('express') 
const cors = require('cors'); 
const app = express() 
const port = 3000 
const schemas = require('./schemas'); 
const middleware = require('./middleware'); 
var bodyParser = require("body-parser"); 

app.use(cors()); 
app.use(bodyParser.json()); 
app.get('/', (req, res) => res.send('Hello World!')) 
app.post('/blog', middleware(schemas.blogPOST) , (req, res) => { 
  console.log('/update'); 
  res.json(req.body); 
}); 
 app.listen(port, () => console.log(`Example app listening on port ${port}!`))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接