express-validatorの利用方法

express-validatorは、expressのミドルウェアとしてリクエストデータをバリデーションする機能を提供してます。内部では、validator.js を利用してます。ここでは、express-validatorの利用方法について確認します。

express-validatorをインストール

$ npm install --save express-validator
  (省略)
$ npm list|grep express-validator
└─┬ express-validator@5.1.2

リクエストデータの検証|validation
( checkモジュールで検証 )

実装例

checkモジュール で検証ルールを設定して、validationResultモジュール で検証結果を確認します。

const express = require('express')
const bodyParser = require('body-parser')
const { check, validationResult } = require('express-validator/check')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.post(
  '/users',

  // バリデーションルール
  [
    check('name').isLength({ min: 5, max: 10}).isAlphanumeric().exists(),
    check('mail').isEmail(),
    check('password').isLength({ min: 5 }),
    check('weekday').not().isIn(['sunday', 'saturday'])
  ],

  (req, res) => {
    // 検証
    const errors = validationResult(req)
    if (!errors.isEmpty()) {
      return res.status(422).json({ errors: errors.array() })
    }

    res.status(200).end()
  }
)

app.listen(5000, () => console.log('Example app listening on port 5000!'))

動作確認

まずは、検証NGの例を確認します。

$ http -v POST localhost:5000/users name=1234 mail=aaa password=1234 weekday=sunday
POST /users HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 72
Content-Type: application/json
Host: localhost:5000
User-Agent: HTTPie/1.0.2

{
    "mail": "aaa",
    "name": "1234",
    "password": "1234",
    "weekday": "sunday"
}

HTTP/1.1 422 Unprocessable Entity
Connection: keep-alive
Content-Length: 308
Content-Type: application/json; charset=utf-8
Date: Sun, 21 Oct 2018 08:51:02 GMT
ETag: W/"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
X-Powered-By: Express

{
    "errors": [
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "name",
            "value": "1234"
        },
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "mail",
            "value": "aaa"
        },
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "password",
            "value": "1234"
        },
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "weekday",
            "value": "sunday"
        }
    ]
}

次に検証OKの例を確認します。

$ http POST localhost:5000/users name=12345 mail=aaa@bbb.com password=12345 weekday=monday
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 0
Date: Sun, 21 Oct 2018 08:15:14 GMT
X-Powered-By: Express

検証対象の調整

リクエストデータの検証対象は下記メソッドで調整できます。

method 検証対象
check([field, message]) req.body req.cookies req.headers req.params req.query
body([fields, message]) req.body
cookie([fields, message]) req.cookies
header([fields, message]) req.headers
param([fields, message]) req.params
query([fields, message]) req.query

参考

リクエストデータの検証|validation
( checkSchemaモジュールで検証 )

実装例

checkSchemaモジュール で検証ルールを設定して、validationResultモジュール で検証結果を確認します。

const express = require('express')
const bodyParser = require('body-parser')
const { checkSchema, validationResult } = require('express-validator/check')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.post(
  '/users',

  // バリデーションルール
  checkSchema({
    name: {
      in: ['body'],
      isLength: {
        options: { min: 5, max: 10 }
      },
      isAlphanumeric: true,
      exists: true
    },
    mail: {
      in: ['body'],
      isEmail: true
    },
    password: {
      in: ['body'],
      isLength: { options: { min: 5 } }
    },
    weekday: {
      in: ['body'],
      isIn: {
        options: ['sunday', 'saturday'],
        negated: true
      }
    }
  }),

  (req, res) => {
    // 検証
    const errors = validationResult(req)
    if (!errors.isEmpty()) {
      return res.status(422).json({ errors: errors.array() })
    }

    res.status(200).end()
  }
)

app.listen(5000, () => console.log('Example app listening on port 5000!'))

動作確認

まずは、検証NGの例を確認します。

$ http -v POST localhost:5000/users name=1234 mail=aaa password=1234 weekday=sunday
POST /users HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 72
Content-Type: application/json
Host: localhost:5000
User-Agent: HTTPie/1.0.2

{
    "mail": "aaa",
    "name": "1234",
    "password": "1234",
    "weekday": "sunday"
}

HTTP/1.1 422 Unprocessable Entity
Connection: keep-alive
Content-Length: 308
Content-Type: application/json; charset=utf-8
Date: Sun, 21 Oct 2018 08:48:21 GMT
ETag: W/"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
X-Powered-By: Express

{
    "errors": [
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "name",
            "value": "1234"
        },
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "mail",
            "value": "aaa"
        },
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "password",
            "value": "1234"
        },
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "weekday",
            "value": "sunday"
        }
    ]
}

次に検証OKの例を確認します。

$ http POST localhost:5000/users name=12345 mail=aaa@bbb.com password=12345 weekday=monday
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 0
Date: Sun, 21 Oct 2018 08:49:57 GMT
X-Powered-By: Express

参考

リクエストデータの無害化|sanitization

実装例

trim を利用すると、前後の余計な空白を削除してくれます。

const express = require('express')
const bodyParser = require('body-parser')
const { check, validationResult } = require('express-validator/check')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.post(
  '/users',

  // バリデーションルール
  [
    check('mail1').isEmail(),
    check('mail2').isEmail().trim(),
    check('mail3').trim().isEmail(),
  ],

  (req, res) => {
    // 検証
    const errors = validationResult(req)
    if (!errors.isEmpty()) {
      return res.status(422).json({ errors: errors.array() })
    }

    res.send({
      mail1: req.body.mail1,
      mail2: req.body.mail2,
      mail3: req.body.mail3
    })
  }
)

app.listen(5000, () => console.log('Example app listening on port 5000!'))

動作確認

下記例では、mailがバリデーションエラーとなります。mail1 は、検証ルールにて trimメソッド を組み込んでないためです。

$ http -v POST localhost:5000/users 'mail1= aaa@bbb.com ' 'mail2= aaa@bbb.com ' 'mail3= aaa@bbb.com '
POST /users HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 78
Content-Type: application/json
Host: localhost:5000
User-Agent: HTTPie/1.0.2

{
    "mail1": " aaa@bbb.com ",
    "mail2": " aaa@bbb.com ",
    "mail3": " aaa@bbb.com "
}

HTTP/1.1 422 Unprocessable Entity
Connection: keep-alive
Content-Length: 94
Content-Type: application/json; charset=utf-8
Date: Sun, 21 Oct 2018 09:10:58 GMT
ETag: W/"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
X-Powered-By: Express

{
    "errors": [
        {
            "location": "body",
            "msg": "Invalid value",
            "param": "mail1",
            "value": " aaa@bbb.com "
        }
    ]
}

mail1 のみ前後の空白を削除してリクエストします。mail2 mail3trimメソッド によって前後の空白が削除されていることを確認できます。

$ http -v POST localhost:5000/users 'mail1=aaa@bbb.com' 'mail2= aaa@bbb.com ' 'mail3= aaa@bbb.com '
POST /users HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 76
Content-Type: application/json
Host: localhost:5000
User-Agent: HTTPie/1.0.2

{
    "mail1": "aaa@bbb.com",
    "mail2": " aaa@bbb.com ",
    "mail3": " aaa@bbb.com "
}

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 67
Content-Type: application/json; charset=utf-8
Date: Sun, 21 Oct 2018 09:14:58 GMT
ETag: W/"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
X-Powered-By: Express

{
    "mail1": "aaa@bbb.com",
    "mail2": "aaa@bbb.com",
    "mail3": "aaa@bbb.com"
}

参考

参考