問題点: async
でリクエストハンドラを定義して処理中に例外が発生してもハンドリングできない
たとえば、http://localhost:3333/notfoundroute
のように存在しないルートにアクセスしたら、ステータスコード:404
、ボディコンテンツにRoute not found
を返したい場合において、以下のコードを実装したとする。
import express, { Request, Response, NextFunction } from "express";
const app = express();
app.get("/hi", (req, res) => {
res.send("Say hi");
});
// 問題の箇所
// ここで例外を発生させる
app.get("*", async (req, res) => {
throw new Error("Route not found");
});
// エラーを扱うためのハンドラ
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
return res.status(404).send(err.message);
});
app.listen(3333, () => console.log("Listenin on port 3333"));
しかし、実行結果としては、レスポンスが返ってこない。
curl -v localhost:3333/notfoundroute
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 3333 (#0)
> GET /notfoundroute HTTP/1.1
> Host: localhost:3333
> User-Agent: curl/7.64.1
> Accept: */*
>
サーバ側のログ出力も以下のようになっていて、Promiseを上手く扱えていない。
(node:11743) UnhandledPromiseRejectionWarning: Error: Route not found
...
(node:11743) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:11743) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
解決策1: next
で例外をラップする。
問題の箇所を以下のように変更する。
...
app.get("*", async (req, res, next) => {
next(new Error("Route not found"));
});
...
実行結果は、期待した結果が得られている。
curl -v localhost:3333/notfoundroute
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 3333 (#0)
> GET /notfoundroute HTTP/1.1
> Host: localhost:3333
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 15
< ETag: W/"f-f4JcZAomq7bAiiL2cdorz+qCRSw"
< Date: Tue, 05 May 2020 07:00:15 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Route not found* Closing connection 0
解決策2: express-async-errors
パッケージをインポートする。
必要なパッケージをインストールする。
yarn add express-async-errors
問題の箇所は変更しなくてもよいが、express-async-errors
をインポートする。
import express, { Request, Response, NextFunction } from "express";
import "express-async-errors";
...
app.get("*", async (req, res) => {
throw new Error("Route not found");
});
...
こちらも実行結果は、期待した結果が得られている。
curl -v localhost:3333/notfoundroute
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 3333 (#0)
> GET /notfoundroute HTTP/1.1
> Host: localhost:3333
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 15
< ETag: W/"f-f4JcZAomq7bAiiL2cdorz+qCRSw"
< Date: Tue, 05 May 2020 07:00:15 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Route not found* Closing connection 0