前回、expressのミドルウェアを色々いじってみたけど、ask-sdkでよく使うようなrequest/response interceptorを試しに実装してみた。
目次
request interceptor
JSONを返すHello Worldをベースにやってみた。requestはとてもかんたん。普通にミドルウェアでconsole.logすればよい。
const express = require('express') const app = express() const port = 3000 const requestInterceptor = (req, res, next) => { const request = {method: req.method, data: null }; request.data = (req.method === "GET") ? req.query : req.body; console.log(JSON.stringify(request)); next(); }; app.use(express.json()) app.use(express.urlencoded({ extended: true })); app.use(requestInterceptor) app.all('/', (req, res) => { res.json({msg: "hello, world"}) }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) })
リクエストメソッドごとに分岐させるようにしてみた。req.params
を使えばマルっと取れるのかなと思ったけど、どうもダメみたい。パスパラメータ用なのかな?あんまりわかってない。
実際に動かしてみるとこうなる。
GET
クライアント
$ curl -X GET http://localhost:3000/?foo=1\&bar=2 {"msg":"hello, world"}
サーバ
$ node hello.js Example app listening on port 3000 {"method":"GET","data":{"foo":"1","bar":"2"}}
GET以外
クライアント
$ curl -X POST 'http://localhost:3000/' -H "Content-type: application/json" -d '{"foo":1,"bar":2 }' {"msg":"hello, world"}
サーバ
{"method":"POST","data":{"foo":1,"bar":2}}
response interceptor
こちらはスッキリとはいかない。レスポンスをミドルウェアでゴニョゴニョしたい、というのは結構多くて、いろいろスレがある。
というのも、
- レスポンスボディは自分で作る。それをconsole.logで出せばいいんだけど、
res.send
が複数箇所あったら面倒。グローバルにやりたい。わかる。 - レスポンスがres.sendやres.jsonで返されるとそこで終わる。レスポンスが返されたあとではもうミドルウェアを実行できない(?)
だからだと思ってる。
で、ざっと見た限り、res.sendやres.jsonをオーバーライドするようなミドルウェアを書くってことみたい。
ということで、書いてみた。
const express = require('express') const app = express() const port = 3000 const requestInterceptor = (req, res, next) => { const request = {method: req.method, data: null }; request.data = (req.method === "GET") ? req.query : req.body; console.log(JSON.stringify(request)); next(); }; const responseInterceptor = (req, res, next) => { const response = {code: res.statusCode, data: null}; let j = res.json; res.json = (data) => { response.data = data; console.log(JSON.stringify(response)); res.json = j; return res.json(data); } next(); }; app.use(express.json()) app.use(express.urlencoded({ extended: true })); app.use(requestInterceptor); app.use(responseInterceptor); app.all('/', (req, res, next) => { res.json({msg: "hello, world"}) }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) })
ということで試してみるとこんな感じ。
# GETの場合 {"method":"GET","data":{"foo":"1","bar":"2"}} {"code":200,"data":{"msg":"hello, world"}} # GET以外の場合 {"method":"POST","data":{"foo":1,"bar":2}} {"code":200,"data":{"msg":"hello, world"}}
まあ一応は動いてる。ただ、エラーの場合、例えば
$ curl -X GET http://localhost:3000/aaa <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Cannot GET /aaa</pre> </body> </html>
みたいな場合のログはこんな感じで表示されない。express側でエラーハンドリングしてくれているので、という理解。
{"method":"GET","data":{}}
その他
スレの中で見つけたんだけど、express-winstonみたいなロギング系のミドルウェアとか、あとはmorgan-bodyみたいなのを使うほうがいいかも。
morgan-bodyを試してみる。
$ npm i --save morgan-body
コード
const express = require('express') const app = express() const port = 3000 const morganBody = require('morgan-body') app.use(express.json()) app.use(express.urlencoded({ extended: true })); morganBody(app); app.all('/', (req, res, next) => { res.json({msg: "hello, world"}) }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) })
こんな感じで出力される。コンソール上はカラーリングされてる。
# GETの場合 Request: GET /?foo=1&bar=2 at Mon Feb 14 2022 03:01:14 GMT+0900, IP: ::ffff:127.0.0.1, User Agent: curl/7.77.0 Response Body: { "msg": "hello, world" } Response: 200 0.555 ms # GET以外の場合 Request: POST / at Mon Feb 14 2022 03:01:22 GMT+0900, IP: ::ffff:127.0.0.1, User Agent: curl/7.77.0 Request Body: { "foo": 1, "bar": 2 } Response Body: { "msg": "hello, world" } Response: 200 0.181 ms # エラーの場合 Request: GET /aa at Mon Feb 14 2022 03:07:06 GMT+0900, IP: ::ffff:127.0.0.1, User Agent: curl/7.77.0 Response: 404 0.470 ms
エラーハンドリングとかはもうちょっと考えないといけない気もするけど、シンプルなものならこれで十分かな。改行とかカラーリングとかの出力はオプションで調整できるみたい。
まとめ
express、ムズカシイ・・・