😺
【Express】APIの例外処理実装と解説
expressを利用した例外処理について解説します。
例外処理
例外処理とは
実行プログラム内でのエラー発生時に、エラーの内容によって実行される処理の事です。
下記に、例外処理の必要性を2つ挙げます。
- エラー時の対応を定義しておかなければサーバダウンなどのリスクが発生する
- 開発側からユーザーに与える情報を定義することで、余計な情報を与えずセキュリティ向上に繋がる
実装
今回は例外発生時に共通のエラークラスを呼び出し、共通エラー処理の関数に飛ばしてレスポンスを返す流れで実装します。
様々なケースが存在しますが、今回はシンプルな実装を意識しています。
CustomException
まず、独自例外と共通エラー処理を定義します。
CostomExceptionクラスが例外発生時にthrowされ、statusCode、message、logLevelの3つを引数として取ってconstructorに渡しています。
その後、CustomExceptionクラスから生成されたerrorオブジェクトとして共通エラー処理で引き取られる形になります。
- statusCode ~ 例外に対応するステータスコード
- message ~ エラーの内容を伝えるメッセージ
- logLevel ~ info, warn, errorなどで例外の種類を定義する。(Zabbixなどで検知する例外を限定する為)
export class CustomException extends Error {
statusCode: number;
logLevel: string;
constructor(statusCode: number, message: string, logLevel: string) {
super(message);
this.statusCode = statusCode;
this.logLevel = logLevel;
Object.setPrototypeOf(this, CustomException.prototype);
}
}
共通エラー処理
例外発生時はcatchでerrorHandler関数を呼び出してCuatomExceptionクラスのインスタンスを処理します。。
CustomExceptionクラスで生成したerrorオブジェクトを使用し、jsonレスポンスとして返却しています。
どの例外処理にも該当しない例外が発生した場合は、500のinternal server errorとして返却するようにしています。
import { Response } from "express";
export const errorHandler = (error: any, res: Response) => {
error.statusCode = error.statusCode || 500;
error.message = error.message || null;
error.logLevel = error.logLevel || null;
if (error instanceof CustomException) {
return res.status(error.statusCode).json({
statusCode: error.statusCode,
message: error.message,
logLevel: error.logLevel,
});
} else {
res.status(500).json({
message: "internal server error",
logLevel: "error",
});
}
};
controller
エラークラスをthrowするcontrollerを定義します。
ifで例外を捕捉し、throw new CustomExceptionで独自エラークラスをインスタンス化、必要な引数を定義しています。
例外発生で処理はcatchに流れ、next(errorHandler())でレスポンスを返却する共通エラークラスに飛ばしています。
export class BoardController {
async allBoard(_req: Request, res: Response): Promise<void> {
const boards = await getBoards();
res.status(200).json({
message: "board get all success",
boards,
});
}
async postBoard(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
const { title, content, boardImage, userId } = req.body;
try {
const board = await createBoard(title, content, boardImage, userId);
if (!board)
throw new CustomException(400, "this board does not create", "info");
res.status(201).json({
message: "this board create is success",
board,
});
} catch (error: any) {
return next(errorHandler(error, res));
}
}
async showBoard(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const id = parseInt(req.params.id);
const board = await getBoard(id);
if (!board)
throw new CustomException(404, "this board does not get", "info");
res.status(200).json({
message: "this board get is success",
board,
});
} catch (error: any) {
return next(errorHandler(error, res));
}
}
}
{
"statusCode": 400,
"message": "this board does not create",
"logLevel": "info"
}
Discussion