初めに
Expressのエラーハンドラは存在しないパスにアクセスした時のエラー(404)はハンドリングしてくれません。
じゃあどうしたらいいか? を調べたのでここにまとめます。
サンプルファイル
ここにExpressを使ったserver.tsファイルを用意します。
ポート3000でサーバを起動し、/usersというルータのみ用意しています。
import express from "express"
const app: express.Express = express()
// サーバ起動
app.listen(3000, () => {
console.log("Start on port 3000.")
})
// ユーザ一覧
app.get("/user", (req: express.Request, res: express.Response) => {
res.send("OK")
})
エラーハンドラを定義してみる
Expressでデフォルトのエラーハンドラをミドルウェアに用意するなら一番下に以下を追記するといいです。
参考: Express エラー処理
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something broke!')
})
(↑引用なのでjsです)
しかしこれだと、存在しないパスにアクセスしたときの404エラーはキャッチしてくれないようです。
試しにサーバを起動して、適当なパスにアクセスしてみます。
エラーハンドラを通っていれば 「Something broke!」 と表示されるはずですが、結果はそうではありません。
404をハンドリングするには
こういう時は、ルーティングの一番最後になんでも通すルーターを用意して、
そこにアクセスが来たら404ページを表示するなどしてハンドリングしたらいいみたいです。
// 404ハンドラー
app.all("*", (req, res) => {
res.send("404!")
})
(↑引用なのでjsです)
もう一度同じページにアクセスしてみると、きちんとハンドリングできたのがわかります。
まとめ
まとめると以下のファイルになります。
import express from "express"
const app: express.Express = express()
// サーバ起動
app.listen(3000, () => {
console.log("Start on port 3000.")
})
// ユーザ一覧
app.get("/user", (req: express.Request, res: express.Response) => {
res.send("OK")
})
// 404ハンドラー
app.all("*", (req, res) => {
res.send("404!")
})
// エラーハンドラ
app.use(
(
err: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
console.error(err.stack)
res.send("Something broke!")
}
)
これで存在しないpathにアクセスした時の404エラーハンドリングができるようになりました。
おまけ Inversify Express Utilsを使ったケース
普段はDIライブラリのInversify Express Utilsを使っています。
この場合は上記のやり方でルーティングを定義すると、@controllerで定義したルーティングよりも app.all()
で定義したルーティングが優先されてしまったりでうまく動きません。
そういう時はこちらのISSUEを参考に以下のように書いてください。
@controller("*")
class NotFoundController {
@httpGet("/")
public async index() {
return Promise.resolve("not found!");
}
}
@controller("/")
class HomeController {
@httpGet("/")
public index() {
return Promise.resolve("home!");
}
}
const container = new Container();
const server = new InversifyExpressServer(container);
const app = server.build();
app.listen(3000, () => {
console.log("Listening on port 3000...");
});
ポイントは @controller("*")
を一番先頭に定義することです。
ミドルウェアを使ったときは一番最後に定義しましたが、inversify-express-utilsを使うときは逆なのでお気をつけください。