[사이드프로젝트] 그저 그런 REST API로 괜찮은가? - 진정한 REST API 구현해보기 - Swagger 연동 및 Profile 링크 추가
Swagger UI
Swagger UI는 다양한 언어, 프레임워크에서 API 문서를 만들고 테스트할 수 있도록 해주는 라이브러리다.
Spring에는 Spring Rest Docs라는 라이브러리가 존재하지만 개인적으로 Swagger UI를 더 선호하기 때문에 강의와 다르게 Swagger UI로 Profile 링크를 만들도록 하겠다.
Swagger 의존성 추가
Maven Repository에 가서 SpringFox Boot Stater 검색해서 라이브러리를 찾은 후 프로젝트에 추가해준다.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Swagger 설정
SwaggerConfig 파일을 만들어서 Swagger 기본 설정을 해준다.
package com.carrykim.restapi.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.HashSet;
import java.util.Set;
@Configuration
@EnableOpenApi
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.OAS_30)
.consumes(getConsumeContentTypes())
.produces(getProduceContentTypes())
.apiInfo(getApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.carrykim.restapi"))
.paths(PathSelectors.ant("/**"))
.build();
}
private Set<String> getConsumeContentTypes() {
Set<String> consumes = new HashSet<>();
consumes.add("application/json;charset=UTF-8");
consumes.add("application/x-www-form-urlencoded");
return consumes;
}
private Set<String> getProduceContentTypes() {
Set<String> produces = new HashSet<>();
produces.add("application/json;charset=UTF-8");
return produces;
}
private ApiInfo getApiInfo() {
return new ApiInfoBuilder()
.title("REST API")
.description("Event REST API")
.contact(new Contact("Carrykim", "https://github.com/gimseonjin", "[email protected]"))
.version("1.0")
.build();
}
}
그리고 EventController로 가서 아래 어노테이션을 붙여준다.
@Api(tags = {"Event Controller"})
@RestController()
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
public class EventController {
private final EventService eventService;
public EventController(EventService eventService) {
this.eventService = eventService;
}
@ApiOperation(value = "Event 객체를 추가하는 메소드")
@PostMapping("")
public ResponseEntity create(@RequestBody @Valid EventDto eventDto) {
...
}
@ApiOperation(value = "모든 Event 객체를 읽어오는 메소드")
@GetMapping("")
public ResponseEntity readAll(Pageable pageable, PagedResourcesAssembler pagedResourcesAssembler) {
...
}
@ApiOperation(value = "Event 단일 객체를 읽어오는 메소드")
@GetMapping("/{id}")
public ResponseEntity read(@PathVariable Integer id){
...
}
@ApiOperation(value = "이벤트 객체를 수정하는 메소드")
@PutMapping("/{id}")
public ResponseEntity update(@RequestBody @Valid EventDto eventDto, @PathVariable Integer id){
...
}
}
Profile 링크 추가
이제 각 응답값에 Profile 링크를 추가해준다.
이때 Swagger의 링크를 가져오기에는 별도의 class를 생성하지 않았기 때문에 methodOn으로 가져올 수 없다.
따라서 new Link()를 통해서 Link 객체를 만들어서 넣어준다.
BaseURL은 ServletUriComponentsBuilder에서 가져오도록 getBaseURL 메소드를 만든다.
@Api(tags = {"Event Controller"})
@RestController()
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
public class EventController {
private final EventService eventService;
public EventController(EventService eventService) {
this.eventService = eventService;
}
@ApiOperation(value = "Event 객체를 추가하는 메소드")
@PostMapping("")
public ResponseEntity create(@RequestBody @Valid EventDto eventDto) {
Event event = this.eventService.create(eventDto);
EventResource eventResource = new EventResource(event);
addLinks(eventResource, "/swagger-ui/index.html#/Event%20Controller/createUsingPOST");
URI uri = linkTo(methodOn(EventController.class)
.create(new EventDto()))
.slash(eventResource.getEvent().getId()).toUri();
return ResponseEntity.created(uri).body(eventResource);
}
@ApiOperation(value = "모든 Event 객체를 읽어오는 메소드")
@GetMapping("")
public ResponseEntity readAll(Pageable pageable, PagedResourcesAssembler pagedResourcesAssembler) {
var result = pagedResourcesAssembler
.toModel(this.eventService.readWithPage(pageable).map(event -> {
EventResource eventResource = new EventResource(event);
addLinks(eventResource, "/swagger-ui/index.html#/Event%20Controller/readUsingGET");
return eventResource;
}));
result.add(new Link(getBaseURL() + "/swagger-ui/index.html#/Event%20Controller/readAllUsingGET","profile"));
return ResponseEntity.ok(result);
}
@ApiOperation(value = "Event 단일 객체를 읽어오는 메소드")
@GetMapping("/{id}")
public ResponseEntity read(@PathVariable Integer id){
Event event = this.eventService.read(id);
EventResource eventResource = new EventResource(event);
addLinks(eventResource, "/swagger-ui/index.html#/Event%20Controller/readUsingGET");
return ResponseEntity.ok(eventResource);
}
@ApiOperation(value = "이벤트 객체를 수정하는 메소드")
@PutMapping("/{id}")
public ResponseEntity update(@RequestBody @Valid EventDto eventDto, @PathVariable Integer id){
Event event = this.eventService.update(id, eventDto);
EventResource eventResource = new EventResource(event);
addLinks(eventResource, "/swagger-ui/index.html#/Event%20Controller/updateUsingPUT");
return ResponseEntity.ok(eventResource);
}
private void addLinks(EventResource eventResource, String profileLink){
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());
eventResource.add(new Link(getBaseURL() + profileLink,"profile"));
}
private String getBaseURL(){
return ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
}
}
Controller Test 수정
이제 각 응답에 Profile이 들어오는 지 확인하기 위해 테스트 코드를 수정한다.
예시
mockMvc.perform(put("/api/events/{id}", event.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(jsonPath("event.name").value(event.getName()))
.andExpect(jsonPath("event.description").value(newDescription))
.andExpect(jsonPath("_links").exists())
// Profile 관련 테스트 추가
.andExpect(jsonPath("_links.profile").exists());
Author And Source
이 문제에 관하여([사이드프로젝트] 그저 그런 REST API로 괜찮은가? - 진정한 REST API 구현해보기 - Swagger 연동 및 Profile 링크 추가), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@carrykim/사이드프로젝트-그저-그런-REST-API로-괜찮은가-진정한-REST-API-구현해보기-Swagger-연동-및-Profile-링크-추가저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)