express-validatorでユーザー登録時にバリデーション処理を行う

#Node.jsでのオススメ書籍

どうもNode.js入門中のものです。

タイトルとは少しそれますが、
まずは、Node.jsを勉強する上で、非常にオススメの書籍をまずは紹介したいと思います。
それがこちらです。

[入門Node.jsプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80Node-js%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-Jonathan-Wexler/dp/4798158623/ref=pd_sbs_14_t_0/358-1701345-7066435?_encoding=UTF8&pd_rd_i=4798158623&pd_rd_r=48c2aaf3-9ef1-4e92-b9c7-a67924b0c29a&pd_rd_w=KSLZR&pd_rd_wg=xULk0&pf_rd_p=ca22fd73-0f1e-4b39-9917-c84a20b3f3a8&pf_rd_r=4ZZSJ61YC2DT8MKKS6BK&psc=1&refRID=4ZZSJ61YC2DT8MKKS6BK)

上記は翻訳版で、原書はこちらになります。

[Get Programming with Node.js](https://www.manning.com/books/get-programming-with-node-js)

Node.jsでMVCパターンを実装し、フロントはテンプレートエンジンで実装するというフロントエンド強強の方には不満な構成かもしれませんが、
基本的なアプリケーションの作り方と、特にセキュリティ関連のバックエンドの実装を学ぶことができるのでとてもオススメです。

~~(AmazonのレビューにあるようにGoogle翻訳すれすれの変な日本語が混ざっていますが、Google翻訳に慣れている方なら苦もなく読めると思います。原書の内容が素晴らしいので、よし)~~

今回はログイン時の実装でバリデーションの実装を行う際に最新バージョンだとはまってしまう箇所があったので、解説したいと思います。

#そもそもバリデーションってなに？

>「バリデーション」とは、「検証、実証、認可、妥当性」を意味する英単語になります。

>例えば、「文書をバリデーション」といった場合には、「記述・入力されたデータが、あらかじめ規定された条件・使用に適合しているか検証・確認する。」ことを表します。
引用：(https://career-picks.com/business-yougo/validation/)

です。今回の実装では、ユーザー登録をする際に、メールアドレスが正しい形式で入力されているか？zipCodeが5ケタで正しく入力されているかをユーザー登録の前にバリデーションします。

#express-validatorをインストール
このバリデーションを実装する方法は様々なアプローチがあるそうですが、本書ではexpress-validatorを用いた方法が紹介されています。
インストールします。

```terminal
npm i express-validator -S
```

最初に本書にある方法で実装してみます。

```javascript:main.js
const express = require("express"),
  app = express(),
  router = express.Router(),
  expressValidator = require("express-validator");

router.use(express.json());

router.use(expressValidator());

router.post(
  '/users/create',
  usersController.validate,
  usersController.create,
  usersController.redirectView,
);

```
```javascript:userController.js
"use strict"
const User = require("../models/user")

// ----（中略）　----- //

module.exports = {
 create: (req, res, next) => {
  if (req.skip) next();
  let userParams = getUserParams(req.body);
  User.create(userParams)
    .then(user => {
      req.flash("success", `${user.fullName}'s account created successfully!`);
      res.locals.redirect = "/users";
      res.locals.user = user;
      next();
    })
    .catch(error => {
      console.log(`Error saving user: ${error.message}`);
      res.locals.redirect = "/users/new";
      req.flash("error", `Failed to create user account because: ${error.message}.`);
      next();
    });
},
redirectView: (req, res, next) => {
  let redirectPath = res.locals.redirect;
  if (redirectPath) res.redirect(redirectPath);
  else next();
},
validate: (req, res, next) => {
  req
    .sanitizeBody("email")
    .normalizeEmail({
      all_lowercase: true
    })
    .trim();
  req.check("email", "Email is invalid").isEmail();
  req
    .check("zipCode", "Zip code is invalid")
    .notEmpty()
    .isInt()
    .isLength({
      min: 5,
      max: 5
    })
    .equals(req.body.zipCode);
  req.check("password", "Password cannot be empty").notEmpty();

  req.getValidationResult().then(error => {
    if (!error.isEmpty()) {
      let messages = error.array().map(e => e.msg);
      req.skip = true;
      req.flash("error", messages.join(" and "));
      res.locals.redirect = "/users/new";
      next();
    } else {
      next();
    }
  });
}
};
```
このようなエラーが出ます。

```terminal
Express Validator Error: expressValidator is not a function
```

#解決方法
expressValidatorは関数として使えません。というエラーです。ググってみたら、v6.0.0から実装が変わっているようです。

(https://translate.google.com/translate?hl=ja&sl=en&tl=ja&u=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F56733975%2Fexpress-validator-error-expressvalidator-is-not-a-function%2F56734897&anno=2)

上記のリンクのように実装しても良いのですが、main.jsの処理が大きくなるので、usersControllerに実装したいと思います。

```javascript:main.js
- expressValidator = require("express-validator");
- router.use(expressValidator());
```

```userController.js
const User = require('../models/user'),
  { check, sanitizeBody, validationResult } = require('express-validator'),

module.exports = {
validate: (req, res, next) => {
    sanitizeBody('email')
      .normalizeEmail({
        all_lowercase: true,
      })
      .trim();
    check('email', 'Email is invalid').isEmail();

    check('zipCode', 'Zip code is invalid')
      .notEmpty()
      .isInt()
      .isLength({
        min: 5,
        max: 5,
      })
      .equals(req.body.zipCode);
    check('password', 'Password cannot be empty').notEmpty();

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      let messages = error.array().map(e => e.msg);
      req.skip = true;
      req.flash('error', messages.join(' and '));

      res.locals.redirect = '/users/new';
      next();
    } else {
      next();
    }
  },
}
```
userController.jsでexpress-validatorをインストールする形にしました。
sanitizeBodyメソッドでメールを全て小文字にし、空白を無くしてから、
checkメソッドで正しい形式・すでに登録されているアドレスではないかをバリデーションしています。
同様に
同様にzipCode,passwordもcheckしています。notEmpty（）は空白ではないかをcheckするメソッドです。

最後にvalidationResultメソッドで、それぞれの形式が正しいか。一致するかを検証し、誤りがあった場合ユーザー作成画面にリダイレクトしています。

#まとめ
すごい良い本なので、Node.jsに興味のある方は読んでみてください。
実装方法やパッケージでもっと良いのがあったらコメントいただけると嬉しいです。

ありがとうございました。