Spring Bootでは例外処理はthrowしてしまえばわりとよしなにやってくれるが…
それでも考えるべきことはある。
場合によっては例外処理をあまり使わず、オブジェクトの戻り値による処理をすることもある。
と、去年の1月からSpring Bootを使い始めてSpringの良さがわかり、Springの深いところに潜り始めた。
気になったところをメモします。
気が向いたら更新しようかと思います。
雑にメモしていたので、気づいた誤りを修正しました。
全てここを参考にさせていただいています。
いろいろ学びが多く、とても良い資料です。
例外処理のパターン@Controller編
基本的には
@Controller
に個別で@ExceptionHandler
@ControllerAdvice
で@ExceptionHandler
- Spring BootのError Controller
3番目は知らなかった
1番目と2番目はよく使うと思う
@Slf4j
@ControllerAdvice("com.example.controllers.foo")
public class GlobalControllerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e, HttpServletResponse response, Model model) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
log.error("Error occurred.", e);
model.addAttribute("errorMessage", e.getMessage());
return "foo/error";
}
}
プレゼンテーション層、404エラーなど全ての例外を補足できない&status codeがセットされないという問題がある
ErrorControllerを使う
Spring MVCでは次の順に呼ばれるとのこと
- ExceptionHandlerExceptionResolver(
@ExceptionHandler
)
- ResponseStatusExceptionResolver(
@ResponseStatus
)
- DefaultHandlerExceptionResolver(
Spring MVC
で起きた例外を処理)
ErrorController
はこれらが適用された後に呼ばれる
例外処理のパターン@RestController編
@RestController
に個別で@ExceptionHandler
@RestControllerAdvice
で@ExceptionHandler
@ResponseStatus
を付与した例外をThrow
ExceptionHandlerを使う場合
@Data
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;
}
public ResponseEntity<ErrorResponse> createResponse(HttpStatus status) {
return new ResponseEntity<ErrorResponse>(this, status);
}
public static ResponseEntity<ErrorResponse> createResponse(BadRequestException e) {
return new ResponseEntity<ErrorResponse>(
new ErrorResponse(e.getKeyName(), e.getKeyValue(), e.getMessage())
,HttpStatus.BAD_REQUEST);
}
}
自前例外(CreateResponseの引数になる)
@Data
public class BadRequestException extends Exception {
private String keyName;
private String keyValue;
private String message;
public BadRequestException(String keyName, String keyValue, String message) {
this.keyName = keyName;
this.keyValue = keyValue;
this.message= message;
}
}
例外ハンドラ
@RestControllerAdvice
public class BadRequestExceptionHandler {
@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> getException(HttpServletRequest req, BadRequestException e) {
return ErrorResponse.createResponse(e);
}
}
なお、
SpringBootの@RestControllerで例外処理をする - Qiita
によると、
既に用意してあるhandlerを継承したほうが良いかもしれない。
@RestControllerAdvice
public class BazExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(MyException.class)
public ResponseEntity<?> handleMyException(MyException ex, WebRequest request) {
return super.handleExceptionInternal(ex, "handleMyException", null, HttpStatus.BAD_REQUEST, request);
}
...snip...
ちなみにですが、
ResponseEntity<?>
はResponseEntity<Object>
やResponseEntity<? extends Object>
とだいたい一緒です。。
つまり何らかのクラスってことです。
細かいことは境界ワイルドカード型(bounded wildcard type)で調べるとわかるかと思います。
Springつかうとこれじゃないと返せないケースがあります。
その他リンク
https://www.baeldung.com/exception-handling-for-rest-with-spring
https://www.slideshare.net/shintanimoto/spring-boot10
https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html