HATEOAS와 Self-Descriptive Message 적용
Spring HATEOAS 소개
import static org.springframework.hateoas.mvc.WebMvcLinkBuilder.*;
public ResponseEntity createEvent(@RequestBody EventDto eventDto, Errors errors) {
.......
Event addEvent = this.eventRepository.save(event);
WebMvcLinkBuilder selfLinkBuilder = linkTo(EventController.class).slash(addEvent.getId());
URI createUri = selfLinkBuilder.toUri();
EventResource eventResource = new EventResource(event);
eventResource.add(linkTo(EventController.class).withRel("query-events"));
eventResource.add(selfLinkBuilder.withSelfRel());
eventResource.add(selfLinkBuilder.withRel("update-event"));
return ResponseEntity.created(createUri).body(eventResource);
}
- “event”로 Wrapping 하지 않기
- @JsonUnwrapped : Jackson에게 어떤 필드의 값에 대해 Wrapping 하지 말고 펼쳐 달라는 의미
import com.fasterxml.jackson.annotation.JsonUnwrapped;
public class EventResource<Event> extends RepresentationModel {
@JsonUnwrapped
private Event event;
public EventResource(Event event) {
this.event = event;
}
public Event getEvent() {
return event;
}
}
- RepresentationModel 대신 EntityModel 상속 받기
import org.springframework.hateoas.Link;
import org.springframework.hateoas.EntityModel;
public class EventResource extends EntityModel<Event> {
@JsonWrapped
private Event event;
public EventResource(Event event) {
this.event = event;
}
}
- Self를 EventResource에 추가
- Event 객체 자신을 나타내는 self를 EventResource에 추가 한다.
- add(linkTo(EventController.class).slash(event.getId()).withSelfRel()) 는 type safe 하다.
- 같은 코드 => add(new Link(“http://localhost:8080/api/events” + event.getId())) 는 Type Safe 하지 않다
- self를 EventResource에 추가 한다.
- eventResource.add(selfLinkBuilder.withSelfRel()); 코드는 제거해도 된다.
import org.springframework.hateoas.RepresentationModel;
import static org.springframework.hateoas.mvc.WebMvcLinkBuilder.linkTo;
public class EventResource extends RepresentationModel<EventResource> {
@JsonUnwrapped
private Event event;
public EventResource(Event event) {
this.event = event;
add(linkTo(EventController.class).slash(event.getId()).withSelfRel());
}
}
- API 인덱스 : IndexController 클래스 작성
@RestController
public class IndexController {
@GetMapping("/api")
public RepresentationModel index() {
var index = new RepresentationModel ();
index.add(linkTo(EventController.class).withRel("events"));
return index;
}
}
- API 인덱스 : ErrorsResource 클래스 작성 / EventController 클래스 수정
- 에러가 발생했을 때에도 Index 링크를 제공하기 위해서 ErrorsResource 클래스를 작성한다.
- EventController에서 Errors 객체를 ErrorsResource에 Wrapping 하여 리턴 합니다.
import org.springframework.hateoas.EntityModel;
import org.springframework.validation.Errors;
public class ErrorsResource extends EntittyModel<Errors> {
@JsonUnwrapped
private Errors errors;
public ErrorsResource(Errors content) {
this.errors = content;
add(linkTo(methodOn(IndexController.class).index()).withRel("index"));
}
}
- API 인덱스 : ErrorsResource 클래스 작성 / EventCotoller 클래스 수정
- EventController에서 Errors 객체를 ErrorsResource에 Wrapping 하여 리턴 한다.
public class EventController {
public ResponseEntity createEvent(@RequestBody @Valid EventDto eventDto, Errors errors) {
if (errors.hasErrors()) {
return ResponseEntity.badRequest().body(new ErrorsResource(errors));
}
eventValidator.validate(eventDto, errors);
if (errors.hasErrors()) {
return ResponseEntity.badRequest().body(new ErrorsResource(errors));
}
}
//createEvent
}