Express: ミドルウェア


前提


ドキュメント


ミドルウェアとしてのコールバック


app.メソッド()によるミドルウェア

app.get('/api/books', (req, res) => {
  res.send('GET')
})
// これだけミドルウェア使用
app.post('/api/books', ipログ, (req, res) => {
  res.send('POST')
})

// ミドルウェア
function ipログ(req, res, next) {
  console.log('IP:', req.ip) // IPアドレスをログ
  next()
}

app.all()

app.all('/api/*', (req, res, next) => {
  // ミドルウェアとしての処理
  next() // 次のコールバックへ
})

app.use()

app.all()と違う点

// ベースが/なので、すべてのパス、メソッドで実行される
app.use((req, res, next) => {
  console.log(req.baseUrl, req.method, req.path)
  next()
})

// ベース/userが一致ならマッチ
app.use('/user', (req, res, next) => {
  console.log(req.baseUrl, req.method, req.path)
  next()
})

ルーターオブジェクト: express.Router()

express.Router()で得られるルーターオブジェクトは、appオブジェクトをモジュール化する役割。

  1. appオブジェクトと同様のメソッドを使える
    • ルーター.メソッド()
  2. 特定のベースパスにマウントできる(.use()メソッドだけ)
    • app.use('/ベースパス', ルーター)

// アプリ全体のオブジェクト
const app = express()

// ルーターオブジェクト
const router = express.Router()
// 別のルーター
const router2 = express.Router()

// /apiにルーターをマウント
app.use('/api', router) // app.all()とかにしないこと

// これはルーター
// /api
router.use((req, res) => {
  res.send([req.baseUrl, req.method, req.path]) // ["/api","GET","/"]
})
// /api/books
router.get('/books', (req, res) => {
  res.send([req.baseUrl, req.method, req.path]) // ["/api","GET","/books"]
})

// これはアプリ全体
// /books
app.get('/books', (req, res) => {
  res.send([req.baseUrl, req.method, req.path]) // ["","GET","/books"]
})

ルーターのモジュール化

router.js

// (中略)
// エクスポート
module.exports = router

app.js

// (中略)
// インポート
const router = require('./router.js')
// マウント
app.use('/api', router)

エラーハンドラ

引数を4つにすることでエラーハンドラになる

app.get('/', (req, res) => {
  // データベースエラーとかで
  throw new Error('サーバエラー') // エラーハンドラへ飛ぶ
})

// 該当ページなし用
app.all('*', (req, res) => {
  // HTTPステータス404でレスポンス
  res.status(404).send('404 Not Found\n')
})

// エラーハンドラ
app.use((err, req, res, next) => {
  console.error(err)
  res.status(500).send('500 Internal Server Error\n')
})

next(エラー)

app.get('/abc', (req, res, next) => {
  next('エラー') // エラーハンドラへ
})

エラーではないnext('route')

app.get('/xyz', (req, res, next) => {
  if(true) next('route')
}, (req, res) => {
  console.log('到達しない')
})
app.get('/xyz', (req, res, next) => {
  // 注意: next('route')でここに飛ぶ
  res.send('到達する\n')
})

ビルトインのミドルウェア

いずれも実行するとミドルウェア関数を戻すので、このように使う。

app.use(express.json())

express.json()

app.use(express.json({
  // デフォルトtrue: -> 配列かオブジェクトのJSONしか受け付けない
  strict: false // 単体のnullなども受付
}))

app.post('/api', (req, res) => {
  res.status(200).json(req.body)
})

クライアント例

curl -X POST --header "Content-Type: application/json" \
   --data '{"key":true}' localhost:3000/api

express.urlencoded()

app.use(express.urlencoded({
  extended: true // デフォルトだが指定で警告抑止
}))

クライアント例

<form action="http://localhost:3000/api" method="post">                                                               
  <input type="text" name="キー">
</form>
# コマンド
curl -X POST --data 'key=value&キー=バリュー' localhost:3000/api

express.static()

const options = {
  // デフォルト
  fallthrough: true, // マッチなければnext()が呼ばれる
  // fallthrough: false, // マッチなければnext(error)
}
// パス /abc.html -> ファイル ./static/abc.html
app.use(express.static(__dirname + '/static', options))

// パス /about -> ファイル ./about/index.html
app.use('/about', express.static(__dirname + '/about', options))

// 該当ページなし用: next()により到達
app.all('*', (req, res) => {
  res.status(404).send('404 Not Found\n')
})

サードパーティのミドルウェア

npmでインストールできるミドルウェアがいくつもある。