Help us understand the problem. What is going on with this article?

express-validator を使ってみたポイント

はじめに

以前、 Node.js + Express.js で Web API を開発した際、 入力チェックに express-validator を使いました。
express-validator は validator.js がベースになっており、 validation や sanitize ができる便利なモジュールですが、提供されている API は文字列に対する検証になるため、 JSON パラメータの型を明確にチェックしたい場合には少し注意が必要でした。
今回は使用した際のポイントなんかを自分の整理がてらメモとして残します。

環境

OS: macOS Mojave 10.14.6
Node.js: 10.16.3
Express.js: 4.16.1
express-validator: 6.2.0

Case1(GET & 桁チェック)

引数 必須
arg1
arg2 min:2 max:4
arg3 max:4
arg4 min:2
case1.js
const express = require('express');
const router = express.Router();
const {check, validationResult} = require('express-validator');

router.get('/', validateParam(), async (req, res) => {
    const errors = validationResult(req);
    if(!errors.isEmpty()) {
        res.status(400).end();
        return;
    }

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

function validateParam() {
    return [
        check('arg1')
            .exists({checkFalsy: true})
            .isString(),
        check('arg2')
            .optional({nullable: true})
            .isInt()
            .isLength({min: 2, max: 4}),
        check('arg3')
            .optional({nullable: true})
            .isInt()
            .isLength({min: undefined, max: 4}),
        check('arg4')
            .optional({nullable: true})
            .isInt()
            .isLength({min: 2, max: undefined})
    ];
}

module.exports = router;

OKな例

?arg1=hoge
?arg1=hoge&arg2=12
?arg1=hoge&arg2=1234
?arg1=hoge&arg3=12
?arg1=hoge&arg3=1
?arg1=hoge&arg3=1234
?arg1=hoge&arg4=12
?arg1=hoge&arg4=1234567890

NGな例

?arg1=hoge&arg2=1
?arg1=hoge&arg2=12345
?arg1=hoge&arg3=12345
?arg1=hoge&arg4=1

解説

GET メソッドを例に桁チェックを行います。
桁は isLength() を使用してチェックできます。
さらに、オプションで min (最小値)と max (最大値)が指定できます。
OKな例とNGな例で、オプションで指定した min と max の境界値が効いているのがわかります。

Case2(POST & 型チェック)

引数 必須
arg1 整数
arg2 文字列の整数
arg3 真偽値
case2.js
const express = require('express');
const router = express.Router();
const {check, validationResult} = require('express-validator');

router.post('/', validateParam(), async (req, res) => {
    const errors = validationResult(req);
    if(!errors.isEmpty()) {
        res.status(400).end();
        return;
    }

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

function validateParam() {
    return [
        check('arg1')
            .exists({nullable: true})
            .not().isString()
            .isInt(),
        check('arg2')
            .optional({nullable: true})
            .isString()
            .isInt(),
        check('arg3')
            .optional({nullable: true})
            .not().isString()
            .not().isInt()
            .isBoolean()
    ];
}

module.exports = router;

OKな例

{
    arg1: 1,
    arg2: '1'
}

{
    arg1: 1,
    arg3: true    
}

NGな例

{
    arg1: 1,
    arg2: 'a'
}

{
    arg1: 1,
    arg2: 1
}

{
    arg1: 1,
    arg3: 'true'
}

{
    arg1: 1,
    arg3: 1
}

解説

POST メソッドを例に型チェックを行います。
冒頭に触れている通り、 express-validator の API は文字列に対する検証なので、少し工夫をします。
arg1 は整数なので、 .not().isString().isInt() を組み合わせて文字列ではないかつ整数という指定になります。
arg2 のように文字列の整数に限定したい場合は、.isString().isInt() を組み合わせて文字列かつ整数という指定になります。
arg3 は真偽値ですが、文字列の 'false', 'true' や整数の 0, 1 が真偽値として判定されないようそれぞれ .not().isString().not().isInt() という指定をしています。ケースによっては上記を真偽値として判定したいという場合もあるので、その場合は適宜指定を外せばOKです。

Case3(POST & 条件付き必須)

引数 必須
arg1 真偽値
arg2 arg1=true の場合のみ必須 文字列
case3.js
const express = require('express');
const router = express.Router();
const {check, oneOf, validationResult} = require('express-validator');

router.post('/', validateParam(), async (req, res) => {
    const errors = validationResult(req);
    if(!errors.isEmpty()) {
        res.status(400).end();
        return;
    }

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

function validateParam() {
    return [
        check('arg1')
            .exists({checkNull: true})
            .not().isString()
            .not().isInt()
            .isBoolean(),
        check('arg2')
            .optional({nullable: true})
            .isString(),
        oneOf(
            [
                check('arg1')
                    .custom((value) => value === false),
                check('arg2')
                    .exists({checkNull: true})
            ]
        )
    ];
}

module.exports = router;

OKな例

{
    arg1: false
}

{
    arg1: true,
    arg2: 'hoge'
}

NGな例

{
    arg1: true
}

解説

POST メソッドを例に条件付き必須チェックを行います。
条件付き必須チェックを行う場合には oneOf() が使用できます。
oneOf() は Validation Chain の配列で、いずれか1つでも真であれば検証OK(=すべて偽だった場合に検証NG)となります。これを利用すると、 arg2 のarg1=true の場合のみ必須という条件は、arg1=falsearg2が必須どちらかが真という条件に置き換えることができます(もっとスマートなやり方があれば教えて下さい)。

まとめ

今回は簡単なサンプルを例にとって紹介しましたが、 express-validator のようなモジュールがあると、組み合わせでたいていの要件を満たすことができてとても便利です。
Web API で入力チェックはとても大切ですが、あまり時間をかけたくないところでもあります。また、実装者が複数いれば実装の粒度も変わりやすいので、実装者に依存しにくくなるというのも大きなメリットでしょうか。

今回、使用したコードはGitHubで公開しています。
https://github.com/ponko2bunbun/express-validator-sample

ponko2bunbun
フリーのソフトウェアエンジニアです。 Node.js や Vue.js を使用することが多いです。 職歴はフリーランス(イマココ:2017.10〜) ← 医薬品企業 ← ベンチャー ← SIer です。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした