kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

expressでrequest/response interceptorを実装してみる

f:id:kun432:20220213014606p:plain

前回、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、ムズカシイ・・・