Spring Boot 예외 처리
예외 처리
Spring Boot에서는 기본으로 다음과 같이 예외가 처리된다.
- REST 클라이언트의 경우
- throw 된 예외의 정보와 HTTP 상태 코드를 포함한 JSON 문자열.
- 브라우저의 경우
- 기본 오류 페이지 (Whilelabel Error Page)
특정 예외가 발생했을 때의 상태 코드 지정하기
코드 작성
src/main/java/sample/springboot/web/MyException.java
package sample.springboot.web;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class MyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MyException(String msg) {
super(msg);
}
}
src/main/java/sample/springboot/web/WebApiController.java
package sample.springboot.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class WebApiController {
@RequestMapping(method=RequestMethod.GET)
public void method1() {
throw new MyException("test exception");
}
}
실행 결과
curl으로 테스트하기
$ curl http://localhost:8080/api
{"timestamp":1430489386562,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}
설명
- 작성한 예외 클래스를 @ResponseStatus에 어노테이션을 부여함으로써, 그 예외가 throw되었을 때의 상태 코드를 지정할 수 있다.
- 브라우저에서 액세스하는 경우 기본 오류 페이지가 표시된다.
모든 예외 처리하기
코드 작성
src/main/java/sample/springboot/web/MyExceptionResolver.java
package sample.springboot.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println(ex.getClass() + " : " + ex.getMessage());
ModelAndView mv = new ModelAndView();
mv.setViewName("my-error");
return mv;
}
}
src/main/resources/templates/my-error.html
<h1>My Error Page</h1>
src/main/java/sample/springboot/web/WebApiController.java
package sample.springboot.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class WebApiController {
@RequestMapping(method=RequestMethod.GET)
public void method1() {
throw new MyException("test exception");
}
@RequestMapping(value="/null", method=RequestMethod.GET)
public void method2() {
throw new NullPointerException("test exception");
}
}
실행 결과
curl으로 테스트하기
$ curl http://localhost:8080/api
{"timestamp":1430490625809,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}
$ curl http://localhost:8080/api/null
<h1>My Error Page</h1>
서버 콘솔 출력
class java.lang.NullPointerException : test exception
설명
HandlerExceptionResolver
을 구현한 클래스를 생성하고, @Component로 컨테이너에 등록한다.- 그러면 컨트롤러에서 예외가 발생하면 등록된 클래스의 resolveException () 메소드가 호출된다.
- 그러나
@ResponseStatus
로 주석 된 클래스가 발생 된 경우는 불리지 않는다. resolveException()
메소드는ModelAndView
를 반환 하도록 되어 있기 때문에 어떤 페이지를 표시 할 수 있다.
Web API 액세스의 경우 json으로 반환하기
코드 작성
src/main/java/sample/springboot/web/HelloController.java
package sample.springboot.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (isRestController(handler)) {
return null;
}
ModelAndView mv = new ModelAndView();
mv.setViewName("my-error");
return mv;
}
private boolean isRestController(Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod)handler;
return method.getMethod().getDeclaringClass().isAnnotationPresent(RestController.class);
}
return false;
}
}
실행 결과
curl으로 테스트하기
$ curl http://localhost:8080/api
{"timestamp":1430490748092,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}
$ curl http://localhost:8080/api/null
{"timestamp":1430490749586,"status":500,"error":"Internal Server Error","exception":"java.lang.NullPointerException","message":"test exception","path":"/api/null"}
설명
- 컨트롤러가
@RestController
어노테이션이 부여된 경우,resolveException()
에서null
을 반환한다. - 그러면 응답이 기본 처리(handling) 방법으로 처리된다 (클라이언트가 curl 같은 브라우저 아니라면 json이 된다).
- 브라우저에서 액세스하는 경우 기본 오류 페이지 (Whitelabel Error Page)가 표시된다.
- 브라우저에서 화면 이동에 접근하려면 @Controller 어노테이션이 부여된 컨트롤러 클래스에 액세스하도록 하고 resolveException()에서 적절한 오류 페이지 날려 주도록한다.
컨트롤러 단위로 예외 처리를 정의하기
코드 작성
src/main/java/sample/springboot/web/WebApiController.java
package sample.springboot.web;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class WebApiController {
@RequestMapping(method=RequestMethod.GET)
public void method1() {
throw new MyException("test exception");
}
@RequestMapping(value="/null", method=RequestMethod.GET)
public void method2() {
throw new NullPointerException("test exception");
}
@ExceptionHandler(NullPointerException.class)
public String handling(NullPointerException e) {
return "{\"message\":\"" + e.getMessage() + "\"}";
}
}
실행 결과
curl으로 테스트하기
$ curl http://localhost:8080/api/null
{"message":"test exception"}
설명
@ExceptionHandler
어노테이션을 메소드에 정의하면, 해당 컨트롤러 내에서만 유효한 예외 핸들링이 가능하다.@ExceptionHandler
의 value는 처리하고자 하는 예외(Exception) Class 객체를 건네 준다.
최종 수정 : 2017-12-17