ASP.NET Core の WebAPI、JSONのシリアライズは自動でやってくれるわ Swagger への対応も簡単だわ便利ですよね。
と、調子に乗って作っていると例外を捕まえ忘れた時
みたいな事になります(UseDeveloperExceptionPageを使わないとステータスコードのみの空ページになりますが)。
やはりエラー時はエラー情報を乗せたJSONを返すようにしたいですよね。
というわけで ExceptionFilter を差し込んで Controller 内で捕まえなかった例外を自動的にエラー用のレスポンスJSONに乗せ換えて返してくれるフィルタのサンプルの作り方です。
独自例外の定義
何の例外が発生しても 500 固定にするならそれはそれでいいんですが、やはりステータスコードやメッセージをある程度制御したい事もあるので、独自の例外を定義します。
public class APIException : Exception {
public int StatusCode_ = 500;
...
public APIException(int statusCode, string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0
)
: this(message, memberName, sourceFilePath, sourceLineNumber) {
StatusCode_ = statusCode;
}
...
}
フィルタの実装
ExceptionFilterAttribute から派生したフィルタの実装を行います。
public class APIExceptionFilter : ExceptionFilterAttribute {
public override void OnException(ExceptionContext context) {
JsonResult result;
if (context.Exception is APIException) {
var ex = context.Exception as APIException;
result = new JsonResult(new {
StatusCode = ex.StatusCode_,
ex.Message
}) {
StatusCode = ex.StatusCode_
};
} else {
result = new JsonResult(new {
StatusCode = 500,
context.Exception.Message
}) {
StatusCode = 500
};
}
context.Result = result;
}
}
捕まえた例外の型が APIException だった時はそのステータスコードとメッセージから、APIException 以外だった時はステータスコード 500と、例外のメッセージからレスポンスを作成するようにします。
フィルタの使用
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<APIExceptionFilter>();
}
[ServiceFilter(typeof(APIExceptionFilter))]
public class ValuesController : Controller
{
...
// GET api/values/ArgumentNullException
[HttpGet("ArgumentNullException")]
public string GetArgumentNullException() {
throw new ArgumentNullException("ArgumentNullException");
}
// GET api/values/APIException
[HttpGet("APIException")]
public string GetAPIException() {
throw new APIException(HttpStatusCode.BadRequest, "Your exception message");
}
}
ConfigureServices でフィルタを ServiceCollection に追加し、Controller でフィルタの使用を宣言します。
Controller で処理しない例外があった場合、フィルタで処理したレスポンスのJSONが返されます。
サンプルソース
サンプルはこちら