API - 예외 처리
HTML 페이지 vs API 오류
API 예외 처리 - ExceptionResolver1
ExceptionHandlerExceptionResolver
@ExceptionHandler을 처리한다. API 예외 처리는 대부분 이 기능으로 해결한다.
ResponseStatusExceptionResolver
HTTP 상태 코드를 지정해준다
예) @ResponseStatus(value = HttpStatus.NOT_FOUND)
DefaultHandlerExceptionResolver
ResponseStatusExceptionResolver
@ResponseStatus 애노테이션을 적용하면 HTTP 상태 코드를 변경해준다.
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류") public class BadRequestException extends RuntimeException {
}
ApiExceptionController - 추가
@GetMapping("/api/response-status-ex1")
public String responseStatusEx1() {
throw new BadRequestException();
}
reason을 MessageSource에서 찾는 기능도 제공한다.
//@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류") @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "error.bad") public class BadRequestException extends RuntimeException {
}
message.properties
error.bad=잘못된 요청 오류입니다. 메시지 사용
ResponseStatusException
ApiExceptionController - 추가
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new
IllegalArgumentException());
}
@ResponseStatusException
@ResponseStatus는 개발자가 직접 변경할 수 없는 예외에는 적용할 수 없다. 이떄는 ResponseStatusException 예외를 사용하면 된다.
ApiExceptionController - 추가
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new
IllegalArgumentException());
}
DefaultHandlerExceptionResolver
ApiExceptionController - 추가
@GetMapping("/api/default-handler-ex")
public String defaultException(@RequestParam Integer data) {
return "ok";
}
@ExceptionHandler
ErrorResult
@Data
@AllArgsConstructor
public class ErrorResult {
private String code;
private String message;
}
ApiExceptionV2Controller
@Slf4j
@RestController
public class ApiExceptionV2Controller {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {
log.error("[exceptionHandle] ex", e);
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandle(Exception e) {
log.error("[exceptionHandle] ex", e); return new ErrorResult("EX", "내부 오류");
}
@GetMapping("/api2/members/{id}")
public MemberDto getMember(@PathVariable("id") String id) {
if (id.equals("ex")) {
throw new RuntimeException("잘못된 사용자"); }
if (id.equals("bad")) {
throw new IllegalArgumentException("잘못된 입력 값");
}
if (id.equals("user-ex")) {
throw new UserException("사용자 오류"); }
return new MemberDto(id, "hello " + id);
}
@Data
@AllArgsConstructor
static class MemberDto {
private String memberId;
private String name;
}
}
@ExceptionHandler 예외 처리 방법
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
우선 순위
@ExceptionHandler(부모예외.class) public String 부모예외처리()(부모예외 e) {}
@ExceptionHandler(자식예외.class) public String 자식예외처리()(자식예외 e) {}
@ExceptionHandler({AException.class, BException.class})
public String ex(Exception e) {
log.info("exception e", e);
}
IllegalArgumentException 처리
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
UserException 처리
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {
log.error("[exceptionHandle] ex", e);
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
Exception
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandle(Exception e) {
log.error("[exceptionHandle] ex", e); return new ErrorResult("EX", "내부 오류");
}
HTMP 오류 화면
@ExceptionHandler(ViewException.class)
public ModelAndView ex(ViewException e) {
log.info("exception e", e);
return new ModelAndView("error");
}
API 예외 처리 - @ControllerAdvice
ExcontrollerAdvice
@Slf4j
@RestControllerAdvice
public class ExControllerAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {
log.error("[exceptionHandle] ex", e);
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandle(Exception e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("EX", "내부 오류"); }
}
ApiExceptionV2Controller
@Slf4j
@RestController
public class ApiExceptionV2Controller {
@GetMapping("/api2/members/{id}")
public MemberDto getMember(@PathVariable("id") String id) {
if (id.equals("ex")) {
throw new RuntimeException("잘못된 사용자");
}
if (id.equals("bad")) {
throw new IllegalArgumentException("잘못된 입력 값"); }
if (id.equals("user-ex")) {
throw new UserException("사용자 오류");
}
return new MemberDto(id, "hello " + id);
}
@Data
@AllArgsConstructor
static class MemberDto {
private String memberId;
private String name;
}
}
@ControllerAdvice
대상 컨트롤러 지정 방법
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class,
AbstractController.class})
public class ExampleAdvice3 {}