ワナビーエンジニアのブログ

なんでもいいから文化的な生活を送りたい

expressのエラーハンドラーは引数の数が4つじゃないとダメ

あけましておめでとうございます。本年もよろしくお願いいたします。(1/31)

今年は一か月に一回のポストが目標です。(1/31)

最近は仕事でnodeを書いており、サーバーはexpressを使っているのですが、documentをちゃんと読まないで痛い目見たという話を書きます。

nodeの関数

まずnode(js)の特徴としてcallbackがあると思います。

function hoge(callback) {
  callback('ho', 'ge');
}

hoge((a, b) => {
  console.log(a); // ho
  console.log(b); // ge
});

このとき、nodeとしては、callbackに何個引数を渡してもOKです。

function hoge(callback) {
  callback('ho', 'ge', 'fuga');
}

hoge((a, b) => {
   console.log(a); // ho 
   console.log(b); // ge
});
function hoge(callback) {
  callback('ho');
}

hoge((a, b) => {
   console.log(a); // ho 
   console.log(b); // undefined
});

つまるところnodeの処理系には、Javaのようなオーバーロード(引数の違いで別の関数とみなす)の仕組みはないです。 また、当然ですが関数に渡された引数は未使用でもよいです。

function hoge(a, b) {
  console.log(a); // ho
  // bはhoge内で未使用
}

hoge('ho');

eslint

nodeの静的解析ツールは、eslintを使用しています。このeslintには、未使用の変数を指摘するno-unused-varがあります。 そしてこのルールはeslintの推奨設定です。 前述の例は下記のように指摘されます。1:18 error 'b' is defined but never used no-unused-vars

便利ですね。hogeはbは使用していないので省略して書けます。

function hoge(a) {
  console.log(a);
}

express

さてepressのerror handlerの話。 express-generatorを使ってひな形を作成した場合下記のエラーハンドラーが生成されます

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

これをeslintにかけると、使ってない変数が指摘されます。36:33 error 'next' is defined but never used no-unused-vars

よしじゃあnext消しますか、と、消しても一見普通に動きます。 が、あるときまったく関係ない部分でエラーがぼろぼろ出るようになりました。。

原因が全然わからず、エラー混入をコミットレベルまで絞った挙句原因分からず、困り果ててAPIドキュメントをみると、思いっきり書いてました。

Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors. 

訳:エラーハンドラーのコールバックに渡す引数の数は、絶対4つにしとけ

ええ。。こんなの知らないお。。。

実装はこのあたりみたいです。 https://github.com/expressjs/express/blob/master/lib/router/layer.js#L62-L68

個人的にはnodeで引数の数を指定するのはイマイチだと思います。が、ドキュメントは隅々まで読みましょう。。 eslintの設定で、no-used-varsのargsをnoneに設定すれば一応指摘は消せます。

あなかしこ

Node.js 超入門

Node.js 超入門

embulk-parser-csv_guessable作った

embulk-parsre-csv_guessableというembulkのプラグインを作りました。

github.com

これは何?

embulk-standardsのcsvパーサを継承して機能拡張したものです。 オリジナルのcsvパーサでは、csvスキーマをconfig.ymlにきっちり書く必要があります。
このためスキーマが変更された場合には動的に対応できず、embulkがコケてしまいます。

以前はこの問題に対応するために、embulk runの前にconfig.ymlを生成するオレオレguessスクリプトをかましていました。 が、

という思いから、"Share & reuse the plugin"の精神でpluginを作ってみることにしました。

やりたいこと

下のような1行目がヘッダ行のcsvをよく見ると思います。(なんか名前あるんでしょうか?)

総務省|政策統括官(統計基準担当)|統計に用いる標準地域コード

ken-code,sityouson-code,tiiki-code,ken-name,sityouson-name1,sityouson-name2,sityouson-name3,yomigana
1,0,1000,北海道,,,,ほっかいどう
1,100,1100,北海道,札幌市,,,さっぽろし
1,101,1101,北海道,札幌市,,中央区,ちゅうおうく
…

このヘッダ行を使って、いい感じにパースしたいというのがこのpluginの発想です。

スキーマ変更に柔軟に対応する

オリジナルのcsvパーサだとオレオレスクリプトを噛ましていましたが、embulk-parser-csv_guessableはヘッダ行をカラム名として利用します。

…
  parser:
    type: csv_guessable
    schema_file: path/to/file_target.csv  # ヘッダのあるファイルを指定する
…

これによって、カラムの挿入や削除といったスキーマ変更にも対応できるだけでなく、
副次的な効果としてダラダラとcolumnsを定義せずとも簡単に書けます。

カラムの型も指定する

parserでlongなりtimestampなりの型にしておきたいことがあると思います。

型はguessによって(簡単には)どうにもなりそうになかったので、指定したい場合は、元のcsvパーサと同じような書き方ができるようにしました。
なお、string型にする場合は書かなくても良いです。(デフォルトstring)

…
  parser:
    type: csv_guessable
    schema_file: path/to/file_target.csv
    columns:
      - {name: 'ken-code', type: long}
      - {name: 'sityouson-code', type: long}
      - {name: 'tiiki-code', type: long}
…

別名にする

特にヘッダが下記のような日本語の場合に、英語名に変更したい事があると思います。

県コード,市町村コード,地域コード,県名,市町村名1,市町村名2,市町村名3,読み仮名
1,0,1000,北海道,,,,ほっかいどう
1,100,1100,北海道,札幌市,,,さっぽろし
1,101,1101,北海道,札幌市,,中央区,ちゅうおうく
…

古き良きcsvといった感じ。。
これは、value_nameというattributeを追加して、英語名に変更できるようにしました。
下記のような設定で、value_namenameに置き換えることができます。

  parser:
    type: csv_guessable
    schema_file: path/to/file_target.csv
    columns:
      - {value_name: '県コード', name: 'ken-code', type: long}
      - {value_name: '市町村コード', 'sityouson-code', type: long}
      - {value_name: '地域コード', name: 'tiiki-code', type: long}
      - {value_name: '県名', name: 'ken-name', type: string}
      - {value_name: '市町村名1', name: 'sityouson-name1', type: string}
      - {value_name: '市町村名2', name: 'sityouson-name2', type: string}
      - {value_name: '市町村名3', name: 'sityouson-name3', type: string}
      - {value_name: '読み仮名', name: 'yomigana', type: string}
…

parserという割にguessしたりfilterっぽい事もしてるきもしますが、こういうcsvはよくあるので自分的には便利なんですよね。
これらのサンプルをgithubに置いてますのでご覧ください。

今後

イケてない部分がまだありますし、なんかもっと上手い方法がある気がしていますので、もう少しメンテ予定。
お気づきの点をリプライなりPRなりコメントいただけたら幸いです :bow:

スポンサードリンク

WEB+DB PRESS Vol.92

WEB+DB PRESS Vol.92

  • 作者: 近藤宇智朗,大和田純,谷口禎英,後藤利博,黒瀧悠太,山下和彦,河野匡貴,古橋貞之,瀬尾直利,菅原元気,吉川崇倫,鈴木康平,星北斗,三宅英明,長野雅広,のざきひろふみ,うらがみ,稲富駿,伊藤直也,うさみけんた,丸山晋平,中島聡,はまちや2,竹原,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2016/04/23
  • メディア: 大型本
  • この商品を含むブログを見る