Form 전송 객체를 분리할 것이기 때문에 Item 객체를 원복시켜준다.
Save 폼을 담당할 ItemSaveForm
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
@Data
public class ItemSaveForm {
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000,max = 1000000)
private Integer price;
@NotNull
@Max(value = 9999)
private Integer quantity;
}
Update 폼을 담당할 ItemUpdateForm
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
@Data
public class ItemUpdateForm {
@NotNull
private Long id;
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000,max = 1000000)
private Integer price;
private Integer quantity;
}
폼을 만들어놨으니 이제 Controller에 넣어서 적용시키자.
@PostMapping("/add")
public String addItem(@Validated @ModelAttribute("item") ItemSaveForm form, BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
if(form.getPrice() != null && form.getQuantity() != null){
int resultPrice = form.getPrice() * form.getQuantity();
if(resultPrice < 10000){
bindingResult.reject("totalPriceMin",new Object[]{10000,resultPrice},null);
}
}
if(bindingResult.hasErrors()){
log.info("target = {}",bindingResult.getTarget());
return "validation/v4/addForm";
}
//성공 로직
Item item = new Item();
item.setItemName(form.getItemName());
item.setPrice(form.getPrice());
item.setQuantity(form.getQuantity());
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v4/items/{itemId}";
}
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable("itemId") Long itemId, Model model){
Item item = itemRepository.findById(itemId);
model.addAttribute("item",item);
return "validation/v4/editForm";
}
@PostMapping("/{itemId}/edit")
public String edit(@PathVariable("itemId")Long itemId, @Validated @ModelAttribute("item")ItemUpdateForm form, BindingResult bindingResult){
if(form.getPrice() != null && form.getQuantity() !=null){
int resultPrice = form.getPrice() * form.getQuantity();
if(resultPrice < 10000){
bindingResult.reject("totalPriceMin",new Object[]{10000,resultPrice},null);
}
}
if(bindingResult.hasErrors()){
log.info("errors={}",bindingResult);
return "validation/v4/editForm";
}
Item itemParam = new Item();
itemParam.setItemName(form.getItemName());
itemParam.setPrice(form.getPrice());
itemParam.setQuantity(form.getQuantity());
itemRepository.update(itemId,itemParam);
return "redirect:/validation/v4/items/{itemId}";
}
@ModelAttribute는 HTTP 요청 파라미터를 다룰 때 사용된다
@RequestBody 는 Http body의 데이터를 객체로 변환할 떄 사용됨 -> 주로 API JSON 요청을 다룰 때 사용된다.
apiController를 만들어서 PostMan을 통해서 테스트 진행
@Slf4j
@RestController
@RequestMapping("/validation/api/items")
public class ValidationItemApiController {
@PostMapping("/add")
public Object addItem(@RequestBody @Validated ItemSaveForm form,
BindingResult bindingResult){
log.info("API 컨트롤러 호출");
if (bindingResult.hasErrors()) {
log.info("검증 오류 발생 errors ={}",bindingResult);
return bindingResult.getAllErrors();
}
log.info("성공 로직 실행");
return form;
}
}
HttpMessageConverter에서 요청 Json을 ItemSaveForm 객체로 생성한다.
HttpMessageConverter는 성공하지만 검증에서 오류가 발생하는 경우
itemSaveForm의 경우 quantity가 최대 9999기 때문에 10000으로 넘겨주게 되면 검증오류가 발생하여 bindingresult.getAllErrorer() 가 ObejctError와 FieldError를 반환해준다.
[
{
"codes": [
"Max.itemSaveForm.quantity",
"Max.quantity",
"Max.java.lang.Integer",
"Max"
],
"arguments": [
{
"codes": [
"itemSaveForm.quantity",
"quantity"
],
"arguments": null,
"defaultMessage": "quantity",
"code": "quantity"
},
9999
],
"defaultMessage": "9999 이하여야 합니다",
"objectName": "itemSaveForm",
"field": "quantity",
"rejectedValue": 10000,
"bindingFailure": false,
"code": "Max"
}
]
HttpMessageConverter는 @ModelAttribute와 다르게 각각의 필드 단위로 적용되지 않고 전체 객체 단위로 적용됨.