快速验证器:分离逻辑

问题描述

这个问题更多地是关于代码组织而不是错误/错误问题。

我正在处理请求正文验证,json 具有如下结构:

  {
      "title": "Beetlejuice","year": "1988","runtime": "92","genres": [
          "Comedy","Fantasy"
      ],"director": "Tim Burton","actors": "Alec Baldwin,Geena Davis,Annie McEnroe,Maurice Page","plot": "A couple of recently deceased ghosts contract the services of a \"bio-exorcist\" in order to remove the obnoxIoUs new owners of their house.","posterUrl": "https://images-na.ssl-images-amazon.com/images/M/MV5BMTUwODE3MDE0MV5BMl5BanBnXkFtZTgwNTk1MjI4MzE@._V1_SX300.jpg"
  }

虽然 json 不大,但对于简单的 POST 请求,验证仍然相当大:

router.post('/api/movies/',body('genres')
        .isArray()
        .withMessage('Property genres should be array of string.').bail()
        .custom(validateGenres).bail(),body('title')
        .not().isEmpty()
        .withMessage('Property title is required.').bail()
        .isstring()
        .withMessage('Property title must be string.').bail()
        .isLength({ max: 255 })
        .withMessage('Property title must have maximum 255 characters.').bail(),body('year')
        .not().isEmpty()
        .withMessage('Property year is required.').bail()
        .isNumeric()
        .withMessage('Property year must be number.').bail(),body('runtime')
        .not().isEmpty()
        .withMessage('Property runtime is required.').bail()
        .isNumeric()
        .withMessage('Property runtime must be number.').bail(),body('director')
        .not().isEmpty()
        .withMessage('Property director is required.').bail()
        .isstring()
        .withMessage('Property director must be string.').bail()
        .isLength({ max: 255 })
        .withMessage('Property director must have maximum 255 characters.').bail(),body('actors')
        .isstring()
        .withMessage('Property actors  must be string.').bail(),body('plot')
        .isstring()
        .withMessage('Property plot must be string.').bail(),body('posterUrl')
        .isstring()
        .withMessage('Property plot must be string.').bail(),(req,res) => {

        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).send(errors.array())
        }

        const movieCreated = movie.create(req.body);
        
        return res.status(201).json(movieCreated);
    }) 

这里的问题是:这种大型验证是否被视为不好的做法?如果是,我是否应该创建一个中间件来验证这个特定的 json,或者有什么可能的架构解决方案来解决这种重构问题?

解决方法

这里的好做法是验证输入✅

您当前方法的缺点是难以长期维持❌

您可以采用中间件的方式,为您需要验证的每个路由/主体创建多个中间件。它有效,但工作量和维护负担会随着时间的推移而增加。

您可以遵循的一种方法是为您的输入创建预期模式(例如,描述预期字段和值的模型定义),并使用验证器根据您创建的模式检查当前输入。

对于 Node.js,我们有多种工具,例如:AJVJOIYUP

使用带有 JOI 的示例示例,您可以通过执行以下操作来替换验证流程:

// ./validation/post-create-user-schema.js
const Joi = require('joi');

const postCreateUserSchema = Joi.object({
  username: Joi.string().alphanum().min(3).max(30).required(),password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(),repeat_password: Joi.ref("password").required(),access_token: [Joi.string(),Joi.number()],birth_year: Joi.number().integer().min(1900).max(2013),email: Joi.string().email({
    minDomainSegments: 2,tlds: { allow: ["com","net"] },}),});

module.exports = { postCreateUserSchema };



// ./routes.js
const { postCreateUserSchema } = require('./validation/post-create-user-schema.js')

router.post('/api/users/',(req,res) => {
  try {
    let json = JSON.parse(req.body);
    postCreateUserSchema.validate(json);
  } catch (err) {
    // -> { value: {},error: '"username" is required' }
    console.error(err);
    return res.status(400).send({ ok: false,message: err.error });
  }

  // ... route code here
});