반응형

@Valid
와 BindingResult
(또는 Errors
)
- BindingResult 는 항상 @Valid 바로 다음 인자로 사용해야 함 (스프링 MVC)
- @NotNull, @NotEmpty, @Min, @Max ... 사용해서 입력값 바인딩 할때 에러 확인이 가능하다
도메인 Validator 만들기
-
Validator 인터페이스 없이 만들어도 상관없다.
@Component public class EventValidator { public void validate(EventDto eventDto, Errors errors) { if (eventDto.getBasePrice() > eventDto.getMaxPrice() && eventDto.getMaxPrice() != 0) { errors.rejectValue("basePrice","wrongValue", "BasePrice is wrong"); errors.rejectValue("maxPrice","wrongValue", "MaxPrice is wrong"); } // errors 에다가 rejectValue 로 에러메세지를 넣어주는것 뿐이다. 간단함 LocalDateTime endEventDateTime = eventDto.getEndEventDateTime(); LocalDateTime closeEnrollmentDateTime = eventDto.getCloseEnrollmentDateTime(); LocalDateTime beginEventDateTime = eventDto.getBeginEventDateTime(); if (endEventDateTime.isBefore(beginEventDateTime) || endEventDateTime.isBefore(closeEnrollmentDateTime) || endEventDateTime.isBefore(eventDto.getBeginEnrollmentDateTime())) { errors.rejectValue("endEventDateTime", "wrongValue", "endEventDataTime is wrong"); } // TODO beginEventDataTime if (beginEventDateTime.isBefore(closeEnrollmentDateTime) || beginEventDateTime.isBefore(eventDto.getBeginEnrollmentDateTime())) { errors.rejectValue("beginEventDateTime", "wrongValue", "beginEventDateTime is wrong"); } // TODO CloseEnrollmentDateTime if (closeEnrollmentDateTime.isBefore(eventDto.getBeginEnrollmentDateTime())) { errors.rejectValue("closeEnrollmentDateTime","wrongValue", "closeEnrollmentDateTime is wrong" ); } } }
Controller 에서 검증
@Controller
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL\_JSON\_VALUE)
public class EventController {
private final EventRepository eventRepository;
private final ModelMapper modelMapper;
//validator를 생성자를 통해 빈을 주입받고
private final EventValidator eventValidator;
//생성자가 하나만있고, 받아올 타입이 빈으로 등록되어있으면 autowired 생략 가능
public EventController(EventRepository eventRepository, ModelMapper modelMapper, EventValidator eventValidator) {
this.eventRepository = eventRepository;
this.modelMapper = modelMapper;
this.eventValidator = eventValidator;
}
// @Valid 를 붙여주면, EventDto의 Entity에 붙어있는 애노테이션을 기반으로 검증을 해준다
// 에러 발생시 Errors에다가 에러의 정보를 담아준다.
@PostMapping
public ResponseEntity createEvent(@RequestBody @Valid EventDto eventDto, Errors errors) {
//@Valid 오류가 난다면, 의존성 추가 : spring-boot-starter-validation 을 해준다.
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
}
//validator로 검증 -> errors에 error를 넣어준다. 에러 발생시 -> badRequest 발생!
eventValidator.validate(eventDto, errors);
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
}
Event event = modelMapper.map(eventDto, Event.class);
Event newEvent = this.eventRepository.save(event);
//link를 생성할땐,
//HATEOAS가 제공하는 linkTo(), methodOn()을 사용 , 지금은 클래스레벨에 RequestMapping이 걸렸기때문에 methodOn 사용 X
URI createUri = linkTo(EventController.class).slash(newEvent.getId()).toUri();
return ResponseEntity.created(createUri).body(event); //201응답을 Uri에 담아서 리턴시킨다.
}
}
테스트 설명 용 애노테이션 만들기
- @Target , @Retention
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface TestDescription {
String value();
}
생성하고 각 테스트 마다 설명을 적어주면 된다. 그러면 테스트를 실행할때, 메소드 이름말고
내가 @TestDescription에 넣어준 문자열이 출력이 되는데.. 난 안됨
테스트 코드 추가
- 끝나는 시간이 시작 시간보다 빠르게 값을 넣어주고, validator가 badRequest를 잘 보내는지 확인!
@Test
@TestDescription("입력 값이 잘못된 경우에 에러가 발생하는 테스트")
public void createEvent_Bad_Request_Wrong_Input() throws Exception {
EventDto eventDto = EventDto.builder()
.name("Spring")
.description("REST API Development with Spring")
.beginEnrollmentDateTime(LocalDateTime.of(2020, 9, 8, 2, 45)) //시작 날짜가 끝나는 날짜보다 빠름!
.closeEnrollmentDateTime(LocalDateTime.of(2020, 9, 7, 2, 45))
.beginEventDateTime(LocalDateTime.of(2020, 9, 10, 2, 45))
.endEventDateTime(LocalDateTime.of(2020, 9, 9, 2, 45))
.basePrice(10000) //maxPrice 보다 큼
.maxPrice(200)
.limitOfEnrollment(100)
.location("Daejoen")
.build();
mockMvc.perform(post("/api/events")
.contentType(MediaTypes.HAL_JSON_VALUE)
.content(objectMapper.writeValueAsString(eventDto))
)
.andDo(print())
.andExpect(status().isBadRequest())
;
}
반응형
반응형

@Valid
와 BindingResult
(또는 Errors
)
- BindingResult 는 항상 @Valid 바로 다음 인자로 사용해야 함 (스프링 MVC)
- @NotNull, @NotEmpty, @Min, @Max ... 사용해서 입력값 바인딩 할때 에러 확인이 가능하다
도메인 Validator 만들기
-
Validator 인터페이스 없이 만들어도 상관없다.
@Component public class EventValidator { public void validate(EventDto eventDto, Errors errors) { if (eventDto.getBasePrice() > eventDto.getMaxPrice() && eventDto.getMaxPrice() != 0) { errors.rejectValue("basePrice","wrongValue", "BasePrice is wrong"); errors.rejectValue("maxPrice","wrongValue", "MaxPrice is wrong"); } // errors 에다가 rejectValue 로 에러메세지를 넣어주는것 뿐이다. 간단함 LocalDateTime endEventDateTime = eventDto.getEndEventDateTime(); LocalDateTime closeEnrollmentDateTime = eventDto.getCloseEnrollmentDateTime(); LocalDateTime beginEventDateTime = eventDto.getBeginEventDateTime(); if (endEventDateTime.isBefore(beginEventDateTime) || endEventDateTime.isBefore(closeEnrollmentDateTime) || endEventDateTime.isBefore(eventDto.getBeginEnrollmentDateTime())) { errors.rejectValue("endEventDateTime", "wrongValue", "endEventDataTime is wrong"); } // TODO beginEventDataTime if (beginEventDateTime.isBefore(closeEnrollmentDateTime) || beginEventDateTime.isBefore(eventDto.getBeginEnrollmentDateTime())) { errors.rejectValue("beginEventDateTime", "wrongValue", "beginEventDateTime is wrong"); } // TODO CloseEnrollmentDateTime if (closeEnrollmentDateTime.isBefore(eventDto.getBeginEnrollmentDateTime())) { errors.rejectValue("closeEnrollmentDateTime","wrongValue", "closeEnrollmentDateTime is wrong" ); } } }
Controller 에서 검증
@Controller
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL\_JSON\_VALUE)
public class EventController {
private final EventRepository eventRepository;
private final ModelMapper modelMapper;
//validator를 생성자를 통해 빈을 주입받고
private final EventValidator eventValidator;
//생성자가 하나만있고, 받아올 타입이 빈으로 등록되어있으면 autowired 생략 가능
public EventController(EventRepository eventRepository, ModelMapper modelMapper, EventValidator eventValidator) {
this.eventRepository = eventRepository;
this.modelMapper = modelMapper;
this.eventValidator = eventValidator;
}
// @Valid 를 붙여주면, EventDto의 Entity에 붙어있는 애노테이션을 기반으로 검증을 해준다
// 에러 발생시 Errors에다가 에러의 정보를 담아준다.
@PostMapping
public ResponseEntity createEvent(@RequestBody @Valid EventDto eventDto, Errors errors) {
//@Valid 오류가 난다면, 의존성 추가 : spring-boot-starter-validation 을 해준다.
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
}
//validator로 검증 -> errors에 error를 넣어준다. 에러 발생시 -> badRequest 발생!
eventValidator.validate(eventDto, errors);
if (errors.hasErrors()) {
return ResponseEntity.badRequest().build();
}
Event event = modelMapper.map(eventDto, Event.class);
Event newEvent = this.eventRepository.save(event);
//link를 생성할땐,
//HATEOAS가 제공하는 linkTo(), methodOn()을 사용 , 지금은 클래스레벨에 RequestMapping이 걸렸기때문에 methodOn 사용 X
URI createUri = linkTo(EventController.class).slash(newEvent.getId()).toUri();
return ResponseEntity.created(createUri).body(event); //201응답을 Uri에 담아서 리턴시킨다.
}
}
테스트 설명 용 애노테이션 만들기
- @Target , @Retention
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface TestDescription {
String value();
}
생성하고 각 테스트 마다 설명을 적어주면 된다. 그러면 테스트를 실행할때, 메소드 이름말고
내가 @TestDescription에 넣어준 문자열이 출력이 되는데.. 난 안됨
테스트 코드 추가
- 끝나는 시간이 시작 시간보다 빠르게 값을 넣어주고, validator가 badRequest를 잘 보내는지 확인!
@Test
@TestDescription("입력 값이 잘못된 경우에 에러가 발생하는 테스트")
public void createEvent_Bad_Request_Wrong_Input() throws Exception {
EventDto eventDto = EventDto.builder()
.name("Spring")
.description("REST API Development with Spring")
.beginEnrollmentDateTime(LocalDateTime.of(2020, 9, 8, 2, 45)) //시작 날짜가 끝나는 날짜보다 빠름!
.closeEnrollmentDateTime(LocalDateTime.of(2020, 9, 7, 2, 45))
.beginEventDateTime(LocalDateTime.of(2020, 9, 10, 2, 45))
.endEventDateTime(LocalDateTime.of(2020, 9, 9, 2, 45))
.basePrice(10000) //maxPrice 보다 큼
.maxPrice(200)
.limitOfEnrollment(100)
.location("Daejoen")
.build();
mockMvc.perform(post("/api/events")
.contentType(MediaTypes.HAL_JSON_VALUE)
.content(objectMapper.writeValueAsString(eventDto))
)
.andDo(print())
.andExpect(status().isBadRequest())
;
}
반응형