MVC 활용(28) : 예외 처리 핸들러 - @ExceptionHandler
28. 예외 처리 핸들러 - @ExceptionHandler
특정 예외가 발생한 요청을 처리하는 핸들러 정의
- 지원하는 메소드 아규먼트 (해당 예외 객체, 핸들러 객체, ...)
- 지원하는 리턴 값
- REST API의 경우 응답 본문에 에러에 대한 정보를 담아주고, 상태 코드를 설정하려면 ResponseEntity를 주로 사용한다.
커스텀한 에러 만들기
public class EventException extends RuntimeException{
}
커스텀한 에러가 발생했을 때 특정한 메시지와 함께 뷰를 보여주고 싶다.
@Controller
@SessionAttributes("event")
public class EventController {
@ExceptionHandler
public String eventErrorHandler(EventException exception, Model model){
model.addAttribute("message", "event error");
return "error";
}
@InitBinder
public void initEventBinder(WebDataBinder webDataBinder){
webDataBinder.setDisallowedFields("id");
webDataBinder.addValidators(new EventValidator());
}
@ModelAttribute
public void categories(Model model){
model.addAttribute("categories", List.of("study", "seminar", "hobby", "social"));
}
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
throw new EventException();
// model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
// return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
attributes.addFlashAttribute("newEvent", event);
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(Model model,
@SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event spring = new Event();
spring.setName("spring");
spring.setLimit(10);
Event newEvent = (Event) model.asMap().get("newEvent");
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(spring);
eventList.add(newEvent);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
에러 페이지
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Error Page</title>
</head>
<body>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
</body>
</html>
또한 가장 구체적인 ExceptionHandler가 매핑된다.
@Controller
@SessionAttributes("event")
public class EventController {
@ExceptionHandler
public String eventErrorHandler(EventException exception, Model model){
model.addAttribute("message", "event error");
return "error";
}
@ExceptionHandler
public String runtimeExceptionHandler(RuntimeException exception, Model model){
model.addAttribute("message", "runtime error");
return "error";
}
@InitBinder
public void initEventBinder(WebDataBinder webDataBinder){
webDataBinder.setDisallowedFields("id");
webDataBinder.addValidators(new EventValidator());
}
@ModelAttribute
public void categories(Model model){
model.addAttribute("categories", List.of("study", "seminar", "hobby", "social"));
}
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
throw new EventException();
// model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
// return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
attributes.addFlashAttribute("newEvent", event);
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(Model model,
@SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event spring = new Event();
spring.setName("spring");
spring.setLimit(10);
Event newEvent = (Event) model.asMap().get("newEvent");
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(spring);
eventList.add(newEvent);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
여러 에러를 같이 처리하고 싶은 경우 : 둘다 받을 수 있는 상위 타입으로 정의
@Controller
@SessionAttributes("event")
public class EventController {
@ExceptionHandler({EventException.class, RuntimeException.class})
public String eventErrorHandler(RuntimeException exception, Model model){
model.addAttribute("message", "runtime error");
return "error";
}
@InitBinder
public void initEventBinder(WebDataBinder webDataBinder){
webDataBinder.setDisallowedFields("id");
webDataBinder.addValidators(new EventValidator());
}
@ModelAttribute
public void categories(Model model){
model.addAttribute("categories", List.of("study", "seminar", "hobby", "social"));
}
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
throw new EventException();
// model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
// return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
attributes.addFlashAttribute("newEvent", event);
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(Model model,
@SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event spring = new Event();
spring.setName("spring");
spring.setLimit(10);
Event newEvent = (Event) model.asMap().get("newEvent");
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(spring);
eventList.add(newEvent);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
Rest API의 경우 상태코드와 에러 메시지를 주기 위해 ResponseEntity를 주로 사용한다.
@RestController
@RequestMapping("/api/events")
public class EventApi {
@ExceptionHandler
public ResponseEntity errorHandler(){
return ResponseEntity.badRequest().body("cant't create event as ...");
}
@PostMapping
public ResponseEntity<Event> createEvent(@RequestBody @Valid Event event, BindingResult bindingResult){
// save event
if(bindingResult.hasErrors()){
bindingResult.getAllErrors().forEach(error -> {
System.out.println(error);
});
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(event);
//return new ResponseEntity<Event>(event, HttpStatus.CREATED); 200이 아닌 201 Created 응답
return ResponseEntity.ok(event);
}
}
참고
- 인프런 : 스프링 웹 MVC(백기선)
- https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-exceptionhandler
Author And Source
이 문제에 관하여(MVC 활용(28) : 예외 처리 핸들러 - @ExceptionHandler), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jsj3282/스프링-MVC-활용28-예외-처리-핸들러-ExceptionHandler저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)