
Expressでコーディングしているとたまに「Cannot set headers after they are sent to the client」と怒られることがあります。原因はお決まりのアレなのですが、今回は簡単なTipsとして紹介します。
エラー事象
サーバーサイドに以下のエラーが出ています。
「(node:54370) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client」
内容としては「クライアント側へレスポンスを返却した後でなぜかヘッダーを設定するような処理をやろうとしているぞ」と怒られています。
問題のあるソースコード
例えば、ある1冊の本のデータを取得する場合に以下のようなソースコードを書いたとします。
app.get('/api/books', auth, async (req, res) => {
const bookId = req.query.id;
try {
const book = await Book.findById(bookId);
if (!book) {
res.status(404).json({
error: {
message: 'Not the book found.'
}
});
}
res.send(book);
} catch (err) {
res.status(400).json({
error: {
message: err.message
}
});
}
});
この時に対象の本のデータが無かった場合、先程のエラー事象が再現します。何が原因なのでしょうか?
原因と対処法
先程のソースコードをよく見ると、本のデータが無かった場合に404でレスポンスを返していますが、実はその後に取得した本のデータ(空)をレスポンスとして返そうとしています。別の言い方をすると、クライアント側に404で返却した後に、空のレスポンスを返却しようとしています。気づきましたか?まさにエラーメッセージの通りで、if文の処理をした際に処理を止めていないことが原因です。
つまり、対象の本が無いことを判定しているif文がそこで処理が終わるようにすれば解決します。
ソースコードを以下のように修正すればエラーは解決できます。
app.get('/api/books', auth, async (req, res) => {
const bookId = req.query.id;
try {
const book = await Book.findById(bookId);
if (!book) {
return res.status(404).json({
error: {
message: 'Not the book found.'
}
});
}
return res.send(book);
} catch (err) {
return res.status(400).json({
error: {
message: err.message
}
});
}
});
気づきにくいですが、分かってしまえば簡単な話ですね。これはExpressに限らず、NodeJSでコーディングしている場合、レスポンスを2回返そうとしていると類似のエラーメッセージが表示されますので、注意しましょう。eslintでは「consistent-return」というルールになっているので、eslintを使っていれば事前に注意してくれますよ。
最後に
いかがでしたか?今回は小さなTipsを紹介しました。Expressでコーディングするとたまに目にする事象なので覚えておくと対処が簡単になります。では。

