エラー処理を一元化し、ミドウェア内で処理をしない
一段落説明
エラー処理専用のオブジェクトがないと、不適切な処理が原因となって重要なエラーが発見されない可能性が高くなります。エラー処理オブジェクトは、エラーを可視化する責任をもちます。例えば、整形されたロガーに書き込んだり、Sentry, Rollbar, Raygun のようなモニタリングサービスにイベントを送信したりするといったことなどです。Express のようなほとんどの Web フレームワークは、エラー処理ミドルウェア機構を提供しています。典型的なエラー処理の流れは以下のようになります。いくつかのモジュールがエラーを投げる -> API router がエラーを捕捉する -> エラー捕捉に責任を持つミドルウェア(例: Express、KOA)にエラーを伝搬する -> 一元化されているエラーハンドラが呼び出される -> ミドルウェアは、補足したエラーが信頼されていないエラーかどうか(操作上のエラーでないか)が伝えられているので、アプリを直ちに再起動することができるようになっています。Express ミドルウェア内でエラー処理をすることは一般的ですが、実際には間違っていることに注意してください ー そうしてしまうと、ウェブ以外のインタフェースで投げられたエラーをカバーすることができません。
コード例 – 典型的なエラーフロー
Javascript
// DAL(データアクセスレイヤー), ここではエラー処理を行いません
DB.addDocument(newCustomer, (error, result) => {
if (error)
throw new Error('Great error explanation comes here', other useful parameters)
});
// API route コード, 同期エラーと非同期エラーの両方を捕捉し、ミドルウェアへ進みます
try {
customerService.addNew(req.body).then((result) => {
res.status(200).json(result);
}).catch((error) => {
next(error)
});
}
catch (error) {
next(error);
}
// エラー処理ミドルウェア、一元化されたエラーハンドラに処理を委譲します
app.use(async (err, req, res, next) => {
const isOperationalError = await errorHandler.handleError(err);
if (!isOperationalError) {
next(err);
}
});
Typescript
// DAL(データアクセスレイヤー), ここではエラー処理を行いません
DB.addDocument(newCustomer, (error: Error, result: Result) => {
if (error)
throw new Error('Great error explanation comes here', other useful parameters)
});
// API route コード, 同期エラーと非同期エラーの両方を捕捉し、ミドルウェアへ進みます
try {
customerService.addNew(req.body).then((result: Result) => {
res.status(200).json(result);
}).catch((error: Error) => {
next(error)
});
}
catch (error) {
next(error);
}
// エラー処理ミドルウェア、一元化されたエラーハンドラに処理を委譲します
app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => {
const isOperationalError = await errorHandler.handleError(err);
if (!isOperationalError) {
next(err);
}
});
コード例 – 専用オブジェクト内でのエラー処理
Javascript
module.exports.handler = new errorHandler();
function errorHandler() {
this.handleError = async (err) => {
await logger.logError(err);
await sendMailToAdminIfCritical;
await saveInOpsQueueIfCritical;
await determineIfOperationalError;
};
}
Typescript
class ErrorHandler {
public async handleError(err: Error): Promise<void> {
await logger.logError(err);
await sendMailToAdminIfCritical();
await saveInOpsQueueIfCritical();
await determineIfOperationalError();
};
}
export const handler = new ErrorHandler();