[横書きアイテム]そんなREST APIで大丈夫ですか?本格的なREST APIの実装を試みる-Swagerを使用してProfileリンクを追加
44791 ワード
Swagger UI
Swager UIは、複数の言語、フレームワークでAPIドキュメントを作成およびテストできるライブラリです.
SpringにはSpring Rest Docsというライブラリが存在しますが、個人的にはSwagger UIが好きなので、講義とは異なりSwager UIを使用してProfileリンクを作成します.
Swager依存項目の追加
SpringFox Boot Staterを検索し、ライブラリを検索してプロジェクトに追加します.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Swagger設定
SwagerConfigファイルを作成し、Swagerプリファレンスを設定します.
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){
...
}
}
プロファイルリンクの追加
各レスポンス値にプロファイルリンクを追加します.
メソッドOnにインポートできません.Swagerのリンクをインポートするために別のクラスが作成されていないためです.
そこでnew Link()でリンクオブジェクトを作成して入れます.
BaseURL getBaseURLメソッドを作成して、サーブレットUriComponentBuilderからインポートします.
@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();
}
}
コントローラテストの変更
テストコードを変更して、各レスポンスに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());
Reference
この問題について([横書きアイテム]そんなREST APIで大丈夫ですか?本格的なREST APIの実装を試みる-Swagerを使用してProfileリンクを追加), 我々は、より多くの情報をここで見つけました https://velog.io/@carrykim/사이드프로젝트-그저-그런-REST-API로-괜찮은가-진정한-REST-API-구현해보기-Swagger-연동-및-Profile-링크-추가テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol