HandlerExceptionResolverComposite에 다음 순서로 등록
1. ExceptionHandlerExceptionResolver
2. ResponseStatusExceptionResolver
3. DefaultHandlerExceptionResolver --> 우선순위가 제일 낮다.
@ExceptionHandler을 처리한다. API 예외 처리는 대부분 이 기능으로 해결한다.
HTTP 상태 코드를 지정해준다.
ex) @ResponseStatus(value = HttpStatus.NOT_FOUND)
스프링 내부 기본 예외를 처리한다.
다음 두 가지 경우를 처리한다.
1. @ResponseStatus가 달려있는 예외
2. ResponseStatusException 예외
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
public class BadRequestException extends RuntimeException{
}
@GetMapping("/api/response-status-ex1")
public String responseStatusEx1() {
throw new BadRequestException();
}
@ResponseStatus 어노테이션을 이용해 HTTP상태 코드를 지정해 줬기 때문에 500에러가 아닌 400에러가 발생한다.
우리가 만든 BadRequestException 예외가 컨트롤러 밖으로 넘어가면 ResponseStatusExceptionResolver 예외가 해당 어노테이션을 확인해서 오류 코드를 httpStatus.BAD_REQUEST(400)으로 변경하고, 메세지도 담는다.
ResponseStatusExceptionResolver 코드를 확인해보면 결국 response.sendError(statusCode,resolvedReason)를 호출하는 것을 확인할 수 있다.
sendError(400)을 호출했기 때문에 WAS에서 다시 오류 페이지 /error를 내부 요청한다.
server:
error:
include-message: always
위와 같이 include-message를 always로 설정하면 메세지까지 출력 가능하다.
error.bad=잘못된 요청 오류입니다. 메세지 사용
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "error.bad")
public class BadRequestException extends RuntimeException{
}
@ResponseStatus는 개발자가 직접 변경할 수 없는 예외에는 적용할 수 없다. (어노테이션을 직접 넣어야 하는데, 내가 코드를 수정할 수 없는 라이브러리의 예외 코드 같은 곳에는 적용할 수 없다. ex) BindException, MethodArgumentNotValidException...)
추가로 어노테이션을 사용하기 때문에 조건에 따라 동적으로 변경하는 것도 어렵다.
이때는 ResponseStatusException 예외를 사용하면 된다.
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new IllegalArgumentException());
}