在使用Mongoose时,使用Joi进行验证是一种好的做法吗?

19

我正在使用Node.js、Mongoose和Koa开发RESTful API,目前在模式和输入验证方面遇到了一些困难。

目前我为每个资源都有一个Mongoose和Joi模式。Mongoose方案仅包括特定资源的基本信息。例如:

const UserSchema = new mongoose.Schema({
  email: {
    type: String,
    lowercase: true,
  },
  firstName: String,
  lastName: String,
  phone: String,
  city: String,
  state: String,
  country: String,
});

Joi模式包含有关对象的每个属性的详细信息:

{
  email: Joi.string().email().required(),
  firstName: Joi.string().min(2).max(50).required(),
  lastName: Joi.string().min(2).max(50).required(),
  phone: Joi.string().min(2).max(50).required(),
  city: Joi.string().min(2).max(50).required(),
  state: Joi.string().min(2).max(50).required(),
  country: Joi.string().min(2).max(50).required(),
}

Mongoose模式用于在写入数据库时,在端点处理程序级别创建给定资源的新实例。

router.post('/', validate, routeHandler(async (ctx) => {
  const userObj = new User(ctx.request.body);
  const user = await userObj.save();

  ctx.send(201, {
    success: true,
    user,
  });
}));

Joi schema 用于在验证中间件中验证用户输入。我为每个资源编写了3个不同的 Joi schema,因为允许的输入根据请求方法(POST、PUT、PATCH)而变化。

async function validate(ctx, next) {
  const user = ctx.request.body;
  const { method } = ctx.request;
  const schema = schemas[method];

  const { error } = Joi.validate(user, schema);

  if (error) {
    ctx.send(400, {
      success: false,
      error: 'Bad request',
      message: error.details[0].message,
    });
  } else {
    await next();
  }
}

我想知道在使用多个Joi模式之上的Mongoose是否是最佳实践,考虑到Mongoose已经具有内置验证功能。如果不是,那么有哪些好的实践方法可以遵循?

谢谢!


2
你能简要解释一下为什么两者都要使用吗?毕竟,Mongoose模式非常强大,您可以在不使用joi的情况下对输入执行复杂的验证。 - omer
3
我考虑将 Joi 验证作为请求级别的中间件,因为 Mongoose 似乎只在创建/保存对象时提供应用级别的验证。 - rok
你能详细说明一下应用层和请求层之间的区别吗? - omer
2
所谓请求级别,是指在请求被接收并在其端点逻辑执行之前。这意味着如果输入未通过验证中间件,则可以立即终止请求。而应用程序级别是指在执行端点逻辑的时候。因此,请求会经过所有中间件,并在对象即将更新到数据库时进行验证。 - rok
@omer 两者是相同的。对吗? - the_haystacker
@the_haystacker 我认为区别在于验证的确切位置。如果我正确理解了rok的答案,他所说的请求级别是指控制器在到达“重型”逻辑代码(即“服务”)之前应该验证请求 - 他称之为“应用程序级别”。我倾向于赞同这种方法。控制器应该进行“浅层”验证:检查字段类型,可能根据某些swagger文件匹配请求等。 “服务”应该负责更复杂的验证。 - omer
2个回答

5

即使您有mongoose模式,实现验证服务是一种常见做法。正如您自己所述,在执行任何数据登录之前,它将返回验证错误。因此,在这种情况下,它肯定可以节省一些时间。 此外,使用joi您可以获得更好的验证控制。但是,它高度取决于您的要求,因为它会增加您必须编写的额外代码,而这可以通过不会对最终结果产生太大影响的方式避免。


0

在我看来,这个问题没有明确的答案。就像上面评论区@omer所说的,Mongoose已经足够强大了。

但是,如果你的代码逻辑/操作在接收输入后非常繁重和昂贵,那么在API层添加额外的保护措施以防止你的重型代码运行也无妨。

编辑:我刚刚发现了这个好的答案,由一位值得尊敬的人提供。


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