API 예외 처리
오류시 HttpStatus 정해주기
예외에 맞춰 HTTP Status Code 를 지정하여 넘겨줄 수 있다.
보통 그냥 API 에러가 나면 500에러가 나는데, 문제에 따라 다른 Status를 남기고 싶을 때 사용한다.
customException
+ @ResponseStatus(code,reason)
조합하기
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
public class BadRequestException extends RuntimeException {
}
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
: Response Status 설정하여 보내준다.
throw ResponseStatusException(HttpStatus,reason,exception)
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new IllegalArgumentException());
}
reason에 message.properties의 key값을 넣을 수도 있다.
reason은 messageSource의 코드값을 넣으면 프로퍼티로도 적용 가능하다.
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "error.bad")
- application.properties
server.error.include-message=always
- messages.properties
error.bad=잘못된 요청 오류입니다.
원리
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
@Override
@Nullable
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
if (ex instanceof ResponseStatusException) {
return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
}
ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
if (status != null) {
return resolveResponseStatus(status, request, response, handler, ex);
}
if (ex.getCause() instanceof Exception) {
return doResolveException(request, response, handler, (Exception) ex.getCause());
}
}
catch (Exception resolveEx) {
if (logger.isWarnEnabled()) {
logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
}
}
return null;
}
}
- Exception에
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
orex instanceof ResponseStatusException
가 있는지 찾는다. - code값과 reason을 가지고 response.sendError(code, reason); 해준다.
- 이후 return new ModelAndView();로 흐름 진행
스프링 내부에서 오류 처리하는 방법
DefaultHandlerExceptionResolver
에서 스프링의 기본 오류를 처리한다.
ex) 인자는 Integer인데 String을 넣은 경우, 500이 아닌 400 에러가 난다.
여러 Exception에 따라 내용이 정의되어 있다.
사용하기 편해졌다. 발전된 형태 @ExceptionHandler
@ExceptionHandler
로 Api 경우에 따라 다른 Status, 오류 Dto Return하기
예외 발생시, http 상태 코드를 수정하고 Exception에 따라 원하는 Error Dto(응답 데이터)를 반환하고 싶다.
구현
RestController의 Exception 처리
@RestControllerAdvice
@Slf4j
public class ExceptionHandleApiController {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResult> illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ResponseEntity<>(new ErrorResult("Test", e.getMessage()), HttpStatus.BAD_REQUEST);
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ResponseEntity<ErrorResult> ExHandle(Exception e) {
log.error("[exceptionHandle] ex", e);
return new ResponseEntity<>(new ErrorResult("Exception", e.getMessage()), HttpStatus.BAD_REQUEST);
}
}
@RestControllerAdvice
: AOP를 Controller에 적용하는 느낌으로, Controller들의 @ExceptionHandler 들을 공통 관리하여 적용할 수 있게 한다. RestController 붙어있는 애들한테만 적용한다.@ExceptionHandler(오류.class)
: 원하는 오류를 처리하겠다 선언한다. 오류값을 비우면 아래 메서드에서 주입받은 Exception을 처리
RestController 이외, 다른 방식으로 처리
1. @ControllerAdvice(annotation = RestController.class)
2. @ControllerAdvice("org.example.controllers.coupon")
3. @ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
1
: Annotation 지정법2
: 패키지 지정법3
: 컨트롤러 지정법
보통 Package 별로 처리하는 것을 많이 사용한다.
실행 순서
- Exception이 Api Controller에서 발생
ExceptionResolver
가 작동한다. 가장 우선순위가 높은 것은ExceptionHandlerResolver
(@ExceptionHandler 적용시 이것)- @ResponseStatus(지정 Status) + 응답 객체로 반환된다.
@ExceptionHandler 특징
- 주입되는 Exception은 모든 자식 예외 객체들 다 잡을 수 있다. 우선순위는 더 디테일한, 자식클래스가 선언되면, 그 자식을 최우선으로 시행된다.
@ExceptionHandler(Exception.class) -> 모든 오류를 처리한다.
@ExceptionHandler(IllegalArgumentsEx.class) -> 더 디테일 하기 때문에, Illegal인 경우는 여기로 빠진다.
- 한번에 여러 오류도 잡을 수 있다.
@ExceptionHandler({AException.class, BException.class})
public String ex(Exception e){} //여러개의 경우 Exception으로 받는다.
- @ExceptionHandler 생략시에는, 주입받는 Exception의 종류를 알아서 받아처리해준다.
'스프링 > MVC' 카테고리의 다른 글
[MVC 기초]Spring Formatter Date, Number 원하는 포맷으로 변환하기 (0) | 2022.07.26 |
---|---|
[MVC 기초] Custom 객체 Converter 생성, ConversionService에 등록해 자유롭게 변환하기 (+thymeleaf에서 변환법) (0) | 2022.07.25 |
[MVC 기본] 공통 관심사항을 처리하는 방법 (Servlet Filter, Spring Interceptor를 구현하며) (0) | 2022.06.06 |
[MVC 기초] 패키지 구조 설계 domain vs web package (0) | 2022.05.29 |
[MVC 기초] Bean Validation (Spring 기능 적극 활용) (0) | 2022.05.26 |