BindingException이나 @Valid, @Validated 검증에서 발생한 BindingResult 안의 에러를 폼에서 어떻게 처리하는가?
바인딩 에러 발생 시 Model에 담기는 정보
@Controller
public class SampleController {
@GetMapping("/events/form")
public String eventsForm(Model model) {
Event newEvent = new Event();
newEvent.setLimit(50);
model.addAttribute("event", newEvent);
return "events/form";
}
@PostMapping("/events")
public String createEvent(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form";
}
return "/events/list";
}
}
public class Event {
private Integer id;
@NotBlank
private String name;
@Min(0)
private Integer limit;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getLimit() {
return limit;
}
public void setLimit(Integer limit) {
this.limit = limit;
}
}
@RunWith(SpringRunner.class)
@WebMvcTest
public class SampleControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void eventForm() throws Exception{
mockMvc.perform(get("/events/form"))
.andDo(print())
.andExpect(view().name("/events/form"))
.andExpect(model().attributeExists("event"));
}
@Test
public void createEvent() throws Exception{
ResultActions result = mockMvc.perform(post("/events")
.param("name", "spring)
.param("limit", "-10"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(model().hasErrors());
ModelAndView mav = result.andReturn().getModelAndView();
Map<String, Object> model = mav.getModel();
System.out.println(model.size());
}
}
디버거로 실행해보면 model에 Event와 BindingResult.event가 들어있다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Create New Event</title>
</head>
<body>
<form action="#" th:action="@{/events}" method="post" th:object="${event}">
<p th:if="${#fields.hasErrors('limit')}" th:errors="*{limit}">Incorrect date</p>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>
<input type="text" title="name" th:field="*{name}"/>
<input type="text" title="limit" th:field="*{limit}"/>
<input type="submit" value="Create"/>
</form>
</body>
</html>
타임리프 사용시 바인딩 에러 보여주기
<p th:if="${#fields.hasErrors('limit')}" th:errors="*{limit}">Incorrect date</p>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Event List</title>
</head>
<body>
<a th:href="@{/events/form}">Create New Event</a>
<div th:unless="${#lists.isEmpty(eventList)}">
<ul th:each="event: ${eventList}">
<p th:text="${event.Name}">Event Name</p>
</ul>
</div>
</body>
</html>
타임리프 목록 보여주기
<a th:href="@{/events/form}">Create New Event</a>
<div th:unless="${#lists.isEmpty(eventList)}">
<ul th:each="event: ${eventList}">
<p th:text="${event.Name}">Event Name</p>
</ul>
</div>
이제 이벤트 목록 Model을 만들어서 View에서 사용할 정보를 채워줘야 한다.
@Controller
public class SampleController {
@GetMapping("/events/form")
public String eventsForm(Model model) {
Event newEvent = new Event();
newEvent.setLimit(50);
model.addAttribute("event", newEvent);
return "events/form";
}
@PostMapping("/events")
public String createEvent(@ModelAttribute @Valid Event event, BindingResult bindingResult, Model model){
if(bindingResult.hasErrors()){
return "/events/form";
}
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
그런데 만약에 목록을 갱신하고 싶어 F5 버튼을 누르면 중복 Submit이 발생한다. 이를 방지하기 위한 패턴으로 post + redirect + get 패턴이 있다.
Post / Redirect / Get 패턴
@Controller
public class SampleController {
@GetMapping("/events/form")
public String eventsForm(Model model) {
Event newEvent = new Event();
newEvent.setLimit(50);
model.addAttribute("event", newEvent);
return "events/form";
}
@PostMapping("/events")
public String createEvent(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form";
}
// 데이터베이스에서 save 했다고 가정
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(Model model){
Event event = new Event();
event.setName("spring");
event.setLimit(10);
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
참고