SpringBootでは@RestController
を使えばお手軽にJSONを返すコントローラーが作れて大変便利。
@RestController
public class HogeController{
@Autowired
EmpRepository empRepository;
@GetMapping
public List<Employee> getEmp(){
return empRepository.findAll();
}
}
こんな感じにしてあげればEmployeeクラスのリストをJSONにして返却してくれる。
でもでも、エラーのときはエラー情報返してあげたい。
そんでもってその時の型はEmployeeのリストじゃなくてエラーメッセージとかエラーコードとかを返したい。
もちろんHTTPステータスもつけたい。
戻り値の型をStringにしてJSON文字列に変換して・・・なんてやったら台無しだし!
というわけでいい感じにエラー情報を返してみましょう。
先にざっくりまとめると、通常処理はExceptionで抜けて例外ハンドリングして別のレスポンス作って返せばOKです。
#1.例外ハンドラーを作成
@ControllerAdvice
と@ExceptionHandler
を使って例外をハンドリングするクラスを作ります。
共通処理にするんじゃなくて、元のクラス専用の例外ハンドリングをしたい場合は、元のクラス内に@ExceptionHandler
を付けたメソッドを作成すればOK。
@ControllerAdvice
public class BadRequestExceptionHandler {
@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> getException(HttpServletRequest req, BadRequestException e){
// エラーレスポンス。のちほど解説
return ErrorResponse.createResponse(e);
}
}
#2.自前のExceptionを作成
これは必須ではないですが、任意のタイミング・内容でExceptionを投げるために作っておくと便利。
上で既に使っているBadRequestException
ですね。
(getter、setterは省略してます。)
public class BadRequestException extends Exception {
private String keyName;
private String keyValue;
private String error;
public BadRequestException(String error, String keyName, String keyValue){
this.keyName = keyName;
this.keyValue = keyValue;
this.error = error;
}
}
エラーとなった項目、その値、エラー情報を渡せるようにしてみました。
#3.エラーレスポンス用のクラスを作成
エラーレスポンス用の情報を入れるクラスを作ります。POJOです。
public class ErrorResponse {
private String KeyName;
private String KeyValue;
private String Message;
public ErrorResponse(String keyName,String keyValue,String message){
this.KeyName = keyName;
this.KeyValue = keyValue;
this.Message = message;
}
}
##3.1.返却するレスポンスについて
レスポンスを作成するにはResponseEntity<T>
を使います。
new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST)
みたいなかんじですね。
これは先ほど作ったErrorResponseクラスの中で生成してもらいましょう。
public ResponseEntity<ErrorResponse> createResponse(HttpStatus status){
return new ResponseEntity<ErrorResponse>(this, status);
}
ついでにExceptionをからResponseEntityを作成するstaticクラスも作ってしまいましょう。
public static ResponseEntity<ErrorResponse> createResponse(BadRequestException e){
return new ResponseEntity<ErrorResponse>(
new ErrorResponse(e.getKeyName(),e.getKeyValue(),e.getMessage())
,HttpStatus.BAD_REQUEST);
}
#4.できた!
ここまで実装してあげたらBadRequestExceptionHandler
が例外をキャッチしてErrorResponse.createResponse(e)
でレスポンスを作成してErrorResponse
のJSONをクライアントに返却できるようになります。
やったね!
#5.一般例外のハンドリング
せっかくなので一般例外もハンドリングします。
リクエストパラメータが受け取り側の変数型に変換できないときに発生するHttpMessageNotReadableException
、
該当URL無しのNoHandlerFoundException
、
GET用処理にPOSTしてきたときなどに発生するHttpRequestMethodNotSupportedException
、
とりあえず全部例外拾っとくかのException
。
これらを追加した例外ハンドラーがこちら。
@ControllerAdvice
public class BadRequestExceptionHandler {
@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> getException(HttpServletRequest req, BadRequestException e){
return ErrorResponse.createResponse(e);
}
/**
* 400 - Bad Reque
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ErrorResponse> notReadable(HttpServletRequest req){
return new ResponseEntity<ErrorResponse>(
new ErrorResponse("All","unknown","NotReadable")
,HttpStatus.BAD_REQUEST);
}
/**
* 404 - Not Found
*/
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<ErrorResponse> notFound(HttpServletRequest req){
return new ResponseEntity<ErrorResponse>(
new ErrorResponse(null,null,"Not Found")
,HttpStatus.NOT_FOUND);
}
/**
* 405 - Method Not Allowed
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ErrorResponse> notSupported(HttpServletRequest req){
return new ResponseEntity<ErrorResponse>(
new ErrorResponse(null,null,"Method Not Allowed")
,HttpStatus.METHOD_NOT_ALLOWED);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> anotherException(Exception e, HttpServletRequest req){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
// ログは、大事
System.err.println("ERROR(500) " + (sdf.format((Calendar.getInstance()).getTime()))
+ ": remoteAddr:" + req.getRemoteAddr()
+ ", remoteHost:" + req.getRemoteHost()
+ ", requestURL:" + req.getRequestURL()
);
e.printStackTrace();
return new ResponseEntity<ErrorResponse>(
new ErrorResponse(null,null,"unknown error")
,HttpStatus.INTERNAL_SERVER_ERROR);
}
}
以上!
#参考
Exception Handling in Spring REST Web Service
https://dzone.com/articles/exception-handling-spring-rest