[사이드프로젝트] 그저 그런 REST API로 괜찮은가? - 진정한 REST API 구현해보기 - Spring HATEOAS 적용
Spring HATEOAS
HATEOAS는 REST 아키텍쳐, Uniform Interface 조건 중 하나로 어플리케이션의 상태를 전이시킬 수 있는 링크, 기능을 제공해야한다.
Spring HATEOAS는 HATEOAS를 만족시키기 위해 만들어진 라이브러리로 크게 링크와 리소스를 만드는 기능, 링크를 찾아주는 기능을 제공한다.
여기서 Resource는 Client가 요구하는 데이터(예를 들어 게시판 글, 이벤트 등)를 포함하고 있으며 Links는 어플리케이션의 다른 상태나 리소스에 접근할 수 있는 링크를 제공한다.
링크에는 크게 2가지 종류가 있으며 HREF, REL가 있다. HREF는 하이퍼링크란 뜻으로 어플리케이션 상태를 전이시킬 수 있는 링크 종류이며 REL은 Resource와 관련된 정보와 기능을 제공하는 링크다.
포함되어야할 Link
EventAPI에서 제공하는 Link에는 다음과 같다.
- profile : API 기술 문서를 나타내는 링크다.
- 지금 API 기술 문서가 없기 때문에 현재는 구현하지 않는다. - self : 현재 리소스의 출처를 나타내는 링크다.
- query-events : 리소스(이벤트)를 검색할 수 있는 링크다.
- update-event : 리소스(이벤트)를 업데이트 할 수 있는 링크다.
EventController Test 수정
@Test
public void create_event_success() throws Exception {
//Given
EventDto eventDto = createEventDto();
//When
//Then
mockMvc.perform(post("/api/events")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaTypes.HAL_JSON)
.content(objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("event").exists())
.andExpect(header().exists(HttpHeaders.LOCATION))
.andExpect(header().string(HttpHeaders.CONTENT_TYPE,MediaTypes.HAL_JSON_VALUE))
//_link 추가
.andExpect(jsonPath("_links.self").exists())
.andExpect(jsonPath("_links.query-events").exists())
.andExpect(jsonPath("_links.update-event").exists());
}
EventResourcs 구현
Spring HATEOAS에서 제공하는 RepresentationModel을 상속받은 EventResource 구현
- RepresentationModel 내부의 add 함수를 통해 link를 추가할 수 있음
package com.carrykim.restapi.event.model.dto;
import com.carrykim.restapi.event.model.Event;
import org.springframework.hateoas.RepresentationModel;
public class EventResource extends RepresentationModel{
private Event event;
public EventResource(Event event){
this.event = event;
}
public Event getEvent() {
return event;
}
}
EventService 수정
Service layer에서 EventResource를 반환하도록 합니다.
package com.carrykim.restapi.event.service;
import com.carrykim.restapi.event.infra.EventRepository;
import com.carrykim.restapi.event.model.Event;
import com.carrykim.restapi.event.model.dto.EventDto;
import com.carrykim.restapi.event.model.dto.EventResource;
import org.springframework.stereotype.Service;
@Service
public class EventService {
private final EventRepository eventRepository;
public EventService(EventRepository eventRepository) {
this.eventRepository = eventRepository;
}
public EventResource create(EventDto eventDto){
Event newEvent = eventDto.toModel();
return new EventResource(eventRepository.save(newEvent));
}
}
EventController 수정
package com.carrykim.restapi.event.controller;
import com.carrykim.restapi.event.model.Event;
import com.carrykim.restapi.event.model.dto.EventDto;
import com.carrykim.restapi.event.model.dto.EventResource;
import com.carrykim.restapi.event.service.EventService;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.net.URI;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController()
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
public class EventController {
private final EventService eventService;
public EventController(EventService eventService) {
this.eventService = eventService;
}
@PostMapping("")
public ResponseEntity create(@RequestBody @Valid EventDto eventDto) {
EventResource eventResource = this.eventService.create(eventDto);
WebMvcLinkBuilder selfAndUpdateLink = linkTo(methodOn(EventController.class)
.create(new EventDto()))
.slash(eventResource.getEvent().getId());
WebMvcLinkBuilder queryLink = linkTo(methodOn(EventController.class));
eventResource.add(queryLink.withRel("query-events"));
eventResource.add(selfAndUpdateLink.withRel("update-event"));
eventResource.add(selfAndUpdateLink.withSelfRel());
URI uri = selfAndUpdateLink.toUri();
return ResponseEntity.created(uri).body(eventResource);
}
}
Test 확인
Author And Source
이 문제에 관하여([사이드프로젝트] 그저 그런 REST API로 괜찮은가? - 진정한 REST API 구현해보기 - Spring HATEOAS 적용), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@carrykim/사이드프로젝트-그저-그런-REST-API로-괜찮은가-진정한-REST-API-구현해보기-Spring-HATEOAS-적용저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)