feng xiaohan

数据验证(Validate)

为什么要验证

在用户使用表单发送数据到后端、后端将数据写入数据库的过程中,如果用户输入了一些无效的数据,可能会影响整个代码的逻辑。(避免提交数据不合法)

如何进行验证

我们可以在不同的地方对数据进行验证:

  • 在客户端进行验证(可选):在发送任何请求到服务端之前,通过 JS 监听输入事件,然后在用户进行表单输入时检查,在页面上进行反馈显示。这可以提高用户的体验

    在客户端验证是可选的,它并不安全。因为客户端使用的 JS 是在浏览器中运行的,用户可以看到、更改或禁用 JS 代码,不利于保护安全数据发送到服务器。

  • 在服务端进行验证(必须):将任何数据存储在数据库之前进行验证,保证存储数据的正确性。并且有些验证功能需要查询数据库,在服务器端进行验证能做的事要多一些。

    用户无法看到、更改或禁用代码,因为这些都发生在服务器上,而不是浏览器中。

  • 数据库内置验证(可选):大多数数据库引擎都有一个内置验证,但是如果在服务端进行了良好的验证,基本上不需要。

在 Node(Express)中进行数据验证

安装验证器

在 Node(Express)中,我们可以安装第三方验证器express-validator来辅助我们进行数据的验证:

npm install --save express-validator

进行验证

引入express-validator/check中的check()check()接收两个参数,第一个参数为作为检查对象的 name;第二个参数是触发校验错误的信息:

const { check } = require('express-validator/check');

const app = express();

app.use('/signup', check('email').isEmail(), authController.postSignup)'); // 通过isEmail()检查渲染页面中name为email的输入选项是否为有效的Email格式

自定义验证

除了直接使用已有的内置验证器函数,我们也使用custom()自定义验证器。

custom()接收一个回调函数,其中回调函数中第一个参数为需要验证的值;第二个参数为一个对象,我们可以从中提取一些信息(如请求、位置、路径):

app.use(
  "/signup",
  check("email")
    .isEmail()
    .withMessage("Please enter a valid email.")
    .custom((value, { req }) => {
      // 自定义验证逻辑
      if (value === "test@test.com") {
        throw new Error("This email address if forbidden."); // 抛出错误异常消息
      }
      return true;
    }),
  authController.postSignup
);
  • 验证再次确认密码

    我们可以用数组,同时注册验证多字段:

    app.use(
      "/signup",
      [
        check("email")
          .isEmail()
          .withMessage("Please enter a valid email.")
          .custom((value, { req }) => {
            if (value === "test@test.com") {
              throw new Error("This email address if forbidden.");
            }
            return true;
          }),
        body(
          "password",
          "Please enter a password with only numbers and text and at least 5 characters."
        )
          .isLength({ min: 5 })
          .isAlphanumeric(),
        body("confirmPassword").custom((value, { req }) => {
          if (value !== req.body.password) {
            // req.body.password获取输入请求的密码
            throw new Error("Password have to match!");
          }
          return true;
        }),
      ],
      authController.postSignup
    );
    

指定验证

在引入check()时,我们可以指定引入传入请求时的一些信息(例如 body、param、query、cookie、header 等),用法和check()相同:

const { check, body } = require('express-validator/check')
...
app.use(
  '/signup',
  [
    check('email')
      .isEmail()
      .withMessage('Please enter a valid email.')
      .custom((value, {req}) => {
        if (value === 'test@test.com') {
          throw new Error('This email address if forbidden.')
        }
        return true;
      }),
    body(
      'password',
      'Please enter a password with only numbers and text and at least 5 characters.'
      )
      .isLength({min: 5})
      .isAlphanumeric()
  ],
  authController.postSignup
);

isLength({min: 5}):最小长度为 5

isAlphanumeric():允许数字和普通字符

验证结果

引入express-validator/check中的validationResult()validationResult()中包含了所有验证的结果:

const { validationResult } = require("express-validator/check");

exports.postSignup = (req, res, next) => {
  const email = req.body.email;
  const password = req.body.password;
  const confirmPassword = req.body.confirmPassword;
  const errors = validationResult(req); // 存储使用验证中间件的请求里的验证结果
  if (!errors.isEmpty()) {
    // 检测errors中是否有错误信息
    console.log(errors.array()); // 打印错误信息数组
    return res.status(422).render("auth/signup", {
      // 返回422状态码,重新返回注册页面
      path: "/signup",
      pageTitle: "Signup",
      errorMessage: errors.array()[0].msg, // 错误信息数组里的每一项的msg为错误信息
    });
  }
};

自定义错误信息

可以在注册中间件函数时添加withMessage()来自定义错误信息:

app.use(
  "/signup",
  check("email").isEmail().withMessage("Please enter a valid email."),
  authController.postSignup
);

附录

express-validator 内置验证器函数

  • isEmail():输入是否为 Email 格式。

  • isURL():输入是否为一个 url 网址形式。

  • isFloat():输入保留一位小数。

  • isLength({}):是否限制输入长度。接收一个对象,对象内设置最长或最短字符数量。

  • withMessage():定义错误信息。

  • custom():自定义验证函数。

  • isAlphanumeric():是否数字和普通字符。

  • isString():是否是字符串形式。

  • normalizeEmail():规范化 Email 格式。

  • trim():删除多余空格。