개발강의정리/Spring

[스프링 기반 REST API 개발] 3-8. API 인덱스 만들기

nineDeveloper 2020. 3. 27.
728x90

스프링 기반 REST API 개발

3. HATEOAS와 Self-Decribtive Message 적용

포스팅 참조 정보

GitHub

공부한 내용은 GitHub에 공부용 Organizations에 정리 하고 있습니다

해당 포스팅에 대한 내용의 GitHub 주소

실습 내용이나 자세한 소스코드는 GitHub에 있습니다
포스팅 내용은 간략하게 추린 핵심 내용만 포스팅되어 있습니다

https://github.com/freespringlecture/spring-rest-api-study/tree/chap03-08_api_index_create

해당 포스팅 참고 인프런 강의

https://www.inflearn.com/course/spring_rest-api/dashboard

실습 환경

  • Java Version: Java 11
  • SpringBoot Version: 2.1.2.RELEASE

8. API 인덱스 만들기

인덱스 핸들러 만들기

웹 페이지를 처음 접근할 때 API의 진입점이 필요함

진입점에 해당하는 index 만들기

인덱스 핸들러

badRequest를 만들때 errors를 받아서 본문에 넣어주는데
ErrorsResources 로 변환하고 Resource로 변환할때 index에 대한 링크를 추가하도록 설정

  • 다른리소스에대한링크제공
  • 문서화
package me.freelife.rest.index;

import me.freelife.rest.events.EventController;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;

@RestController
public class IndexController {

    @GetMapping("/api")
    public ResourceSupport index() {
        var index = new ResourceSupport();
        index.add(linkTo(EventController.class).withRel("events"));
        return index;
    }
}

인덱스 핸들러 테스트코드 추가

package me.freelife.rest.index;

import me.freelife.rest.common.RestDocsConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@Import(RestDocsConfiguration.class)
@ActiveProfiles("test")
public class IndexControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void index() throws Exception {
        this.mockMvc.perform(get("/api/"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("_links.events").exists());
    }

}

테스트 컨트롤러 리팩토링

중복코드제거

에러 리소스

에러 발생 시 인덱스로 가는 링크 제공

EventResource 클래스 생성

package me.freelife.rest.common;

import me.freelife.rest.index.IndexController;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.validation.Errors;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

public class ErrorsResource extends Resource<Errors> {
    public ErrorsResource(Errors content, Link... links) {
        super(content, links);
        add(linkTo(methodOn(IndexController.class)).withRel("index"));
    }
}

EventController 리팩토링

import me.freelife.rest.common.ErrorsResource;

...

@PostMapping
public ResponseEntity createEvent(@RequestBody @Valid EventDto eventDto, Errors errors) {
    if(errors.hasErrors())
        return badRequest(errors); //return ResponseEntity.badRequest().body(errors);

    eventValidator.validate(eventDto, errors);
    if(errors.hasErrors()) {
        return badRequest(errors); //return ResponseEntity.badRequest().body(errors);
    }

    ...
  }

  //badRequest 메서드를 생생 후 ErrorsResource를 사용해서 리팩토링
  private ResponseEntity badRequest(Errors errors) {
      return ResponseEntity.badRequest().body(new ErrorsResource(errors));
  }
}

ErrorsResource unwrapping 문제

스프링 HATEOAS 적용에서 처리했던 것과 비슷한 부분 @JsonUnwrapped로 했던 부분

스프링 부트가 제공해주는 ResorceSupport 하위에 extends Resource<T>로 처리함

JSON 이라서 unwrapped가 되었는데 여기서는 contentJSON Arrays여서 unwrapped가 안되고
contentwrapping되므로 content[0]의 값을 검증하도록 테스트 코드 변경


...

public class EventControllerTests {

  ...

    @Test
    @TestDescription("입력 값이 잘못된 경우에 에러가 발생하는 테스트")
    public void createEvent_Bad_Request_Wrong_Input() throws Exception {

                ...

                .andExpect(jsonPath("content[0].objectName").exists())
                .andExpect(jsonPath("content[0].defaultMessage").exists())
                .andExpect(jsonPath("content[0].code").exists())
                .andExpect(jsonPath("_links.index").exists());
    }
}
728x90

댓글

💲 추천 글