1. 개요

이 기사는 Spring에서 REST설정 하는 방법 – 컨트롤러 및 HTTP 응답 코드, 페이로드 마샬링 구성 및 콘텐츠 협상을 보여줍니다.

2. Spring에서의 REST 이해

Spring 프레임 워크는 RESTful 서비스를 생성하는 두 가지 방법을 지원합니다.

  • ModelAndView 와 함께 MVC 사용
  • HTTP 메시지 변환기 사용

의 ModelAndView 접근 방식은 나이가 훨씬 더 문서화뿐만 아니라, 더 자세한 정보 및 구성 무겁습니다. REST 패러다임을 이전 모델로 통합하려고 시도하지만 문제가없는 것은 아닙니다. Spring 팀은 이것을 이해하고 Spring 3.0부터 일류 REST 지원을 제공했습니다.

HttpMessageConverter  및 어노테이션을 기반으로하는 새로운 접근 방식 은 훨씬 더 가볍고 구현하기 쉽습니다. 구성은 최소한이며 RESTful 서비스에서 기대할 수있는 적절한 기본값을 제공합니다.

3. 자바 구성

@Configuration
@EnableWebMvc
public class WebConfig{
   //
}

새로운 @EnableWebMvc 어노테이션은 몇 가지 유용한 작업을 수행합니다. 특히 REST의 경우 클래스 경로에서 Jackson 및 JAXB 2의 존재를 감지하고 기본 JSON 및 XML 변환기를 자동으로 만들고 등록합니다. 어노테이션의 기능은 XML 버전과 동일합니다.

<mvc : 어노테이션 기반 />

이것은 지름길이며 많은 상황에서 유용 할 수 있지만 완벽하지는 않습니다. 더 복잡한 구성이 필요한 경우 어노테이션을 제거하고 WebMvcConfigurationSupport를 직접 확장 하십시오 .

3.1. Spring Boot 사용

우리가 사용하는 경우 @SpringBootApplication의 어노테이션과  스프링 webmvc의  라이브러리가 클래스 경로에 다음  @EnableWebMvc의 어노테이션에 자동으로 추가됩니다 기본 자동 .

@Configuration  어노테이션이있는 클래스  에 WebMvcConfigurer 인터페이스를  구현하여이 구성에 MVC 기능을 추가 할 수 있습니다  . WebMvcRegistrationsAdapter 인스턴스를 사용하여 자체 RequestMappingHandlerMapping , RequestMappingHandlerAdapter 또는 ExceptionHandlerExceptionResolver  구현 을 제공  할 수도 있습니다  .

마지막으로 Spring Boot의 MVC 기능을 버리고 사용자 정의 구성을 선언하려면 @EnableWebMvc 어노테이션 을 사용하여 그렇게 할 수 있습니다  .

4. 스프링 컨텍스트 테스트

Spring 3.1부터 @Configuration 클래스에 대한 최고 수준의 테스트 지원을받습니다 .

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( 
  classes = {WebConfig.class, PersistenceConfig.class},
  loader = AnnotationConfigContextLoader.class)
public class SpringContextIntegrationTest {

   @Test
   public void contextLoads(){
      // When
   }
}

@ContextConfiguration 어노테이션을 사용하여 Java 구성 클래스를 지정합니다 . 새로운 AnnotationConfigContextLoader@Configuration 클래스 에서 빈 정의를로드합니다 .

것을 알 수 WebConfig의 가 제공되지 않는 서블릿 컨텍스트에서 실행해야하기 때문에 구성 클래스가 테스트에 포함되지 않았습니다.

4.1. Spring Boot 사용

Spring Boot는 보다 직관적 인 방식으로 테스트를 위해 Spring ApplicationContext 를 설정하는 몇 가지 어노테이션을 제공합니다 .

애플리케이션 구성의 특정 슬라이스 만로드하거나 전체 컨텍스트 시작 프로세스를 시뮬레이션 할 수 있습니다.

예를 들어 서버를 시작하지 않고 전체 컨텍스트를 생성 하려면 @SpringBootTest 어노테이션을 사용할 수 있습니다  .

그런 다음 @AutoConfigureMockMvc  를 추가하여 MockMvc  인스턴스 를 주입하고  HTTP 요청을 보낼 수 있습니다 .

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class FooControllerAppIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void whenTestApp_thenEmptyResponse() throws Exception {
        this.mockMvc.perform(get("/foos")
            .andExpect(status().isOk())
            .andExpect(...);
    }

}

전체 컨텍스트 생성을 방지하고 MVC 컨트롤러 만 테스트하려면 @WebMvcTest 를 사용할 수 있습니다  .

@RunWith(SpringRunner.class)
@WebMvcTest(FooController.class)
public class FooControllerWebLayerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private IFooService service;

    @Test()
    public void whenTestMvcController_thenRetrieveExpectedResult() throws Exception {
        // ...

        this.mockMvc.perform(get("/foos")
            .andExpect(...);
    }
}

이 주제에 대한 자세한 정보는 'Testing in Spring Boot'기사에서 찾을 수 있습니다 .

5. 컨트롤러

@RestController는 평온한 API의 전체 웹 계층의 중심 유물이다. 이 게시물의 목적을 위해 컨트롤러는 간단한 REST 리소스 Foo를 모델링하고 있습니다 .

@RestController
@RequestMapping("/foos")
class FooController {

    @Autowired
    private IFooService service;

    @GetMapping
    public List<Foo> findAll() {
        return service.findAll();
    }

    @GetMapping(value = "/{id}")
    public Foo findById(@PathVariable("id") Long id) {
        return RestPreconditions.checkFound(service.findById(id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Long create(@RequestBody Foo resource) {
        Preconditions.checkNotNull(resource);
        return service.create(resource);
    }

    @PutMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void update(@PathVariable( "id" ) Long id, @RequestBody Foo resource) {
        Preconditions.checkNotNull(resource);
        RestPreconditions.checkNotNull(service.getById(resource.getId()));
        service.update(resource);
    }

    @DeleteMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void delete(@PathVariable("id") Long id) {
        service.deleteById(id);
    }

}

내가 직관적 인 Guava 스타일 RestPreconditions 유틸리티를 사용하고 있음을 눈치 채 셨을 것입니다 .

public class RestPreconditions {
    public static <T> T checkFound(T resource) {
        if (resource == null) {
            throw new MyResourceNotFoundException();
        }
        return resource;
    }
}

컨트롤러 구현은 공개되지 않습니다. 이는 공개 할 필요가 없기 때문입니다.

일반적으로 컨트롤러는 의존성 체인에서 마지막입니다. Spring 프론트 컨트롤러 ( DispatcherServlet ) 로부터 HTTP 요청을 수신 하고 단순히 서비스 계층에 전달합니다. 컨트롤러를 직접 참조를 통해 주입하거나 조작해야하는 사용 사례가없는 경우 공개로 선언하지 않는 것이 좋습니다.

요청 매핑은 간단합니다. 다른 컨트롤러와 마찬가지로 매핑 의 실제 과 HTTP 메서드는 요청의 대상 메서드를 결정합니다. @ RequestBody 는 메서드의 매개 변수를 HTTP 요청의 본문에 바인딩하는 반면 @ResponseBody 는 응답 및 반환 유형에 대해 동일한 작업을 수행합니다.

@RestController는  A는 속기 둘 다 포함 할 수  @ResponseBody @Controller 우리의 클래스에 어노테이션을 .

또한 리소스가 올바른 HTTP 변환기를 사용하여 마샬링되고 마샬링되지 않도록합니다. 콘텐츠 협상은 대부분 Accept 헤더를 기반으로 사용할 활성 변환기 중 하나를 선택하기 위해 발생 하지만, 다른 HTTP 헤더도 표현을 결정하는 데 사용될 수 있습니다.

6. HTTP 응답 코드 매핑

HTTP 응답의 상태 코드는 REST 서비스에서 가장 중요한 부분 중 하나이며 주제는 금방 매우 복잡해질 수 있습니다. 이러한 권리를 얻는 것은 서비스를 만들거나 중단시킬 수 있습니다.

6.1. 매핑되지 않은 요청

Spring MVC가 매핑이없는 요청을 받으면 요청이 허용되지 않는 것으로 간주하고 405 METHOD NOT ALLOWED를 클라이언트에 반환합니다.

허용 되는 작업을 지정하기 위해 클라이언트에 405반환 할 때 Allow HTTP 헤더 를 포함하는 것도 좋은 방법 입니다. 이것은 Spring MVC의 표준 동작이며 추가 구성이 필요하지 않습니다.

6.2. 유효한 매핑 된 요청

매핑이있는 요청에 대해 Spring MVC는 요청이 유효한 것으로 간주하고 다른 상태 코드가 지정되지 않으면 200 OK로 응답합니다.

이 컨트롤러가 다른 선언 있음이 때문에의 @ResponseStatus (가)에 대한 생성 , 갱신삭제 에 대한 작업이 아닌 GET 실제로 기본 200 OK를 반환해야합니다.

6.3. 클라이언트 오류

클라이언트 오류의 경우 사용자 지정 예외가 정의되고 적절한 오류 코드에 매핑됩니다.

웹 계층의 모든 계층에서 이러한 예외를 던지기 만하면 Spring이 HTTP 응답에 해당 상태 코드를 매핑 할 수 있습니다.

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
   //
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
   //
}

이러한 예외는 REST API의 일부이므로 REST에 해당하는 적절한 계층에서만 사용해야합니다. 예를 들어 DAO / DAL 계층이 존재하는 경우 예외를 직접 사용해서는 안됩니다.

또한 이것들은 체크 된 예외가 아니라 런타임 예외라는 점에 유의하십시오 – Spring 관행 및 관용구와 일치합니다.

6.4. @ExceptionHandler 사용

특정 상태 코드에 대한 사용자 지정 예외를 매핑하는 또 다른 옵션 은 컨트롤러에서 @ExceptionHandler 어노테이션 을 사용하는 입니다. 이 접근 방식의 문제점은 어노테이션이 정의 된 컨트롤러에만 적용된다는 것입니다. 즉, 각 컨트롤러에서 개별적으로 선언해야합니다.

물론 더 많은 유연성을 제공하는 Spring과 Spring Boot 모두에서 오류를 처리 하는 더 많은 방법이 있습니다 .

7. 추가 Maven 의존성

표준 웹 애플리케이션에 필요한 spring-webmvc 의존성 외에도 REST API에 대한 콘텐츠 마샬링 및 언 마샬링을 설정해야합니다.

<dependencies>
   <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.8</version>
   </dependency>
   <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.3.1</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

REST 리소스의 표현을 JSON 또는 XML로 변환하는 데 사용되는 라이브러리입니다.

7.1. Spring Boot 사용

JSON 형식의 리소스를 검색하려는 경우 Spring Boot는 Jackson, Gson 및 JSON-B와 같은 다른 라이브러리에 대한 지원을 제공합니다.

자동 구성은 클래스 경로에 매핑 라이브러리를 포함하여 수행됩니다.

일반적으로 웹 애플리케이션을 개발하는 경우  spring-boot-starter-web 의존성을 추가하고 여기에 의존하여 프로젝트에 필요한 모든 아티팩트를 포함합니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.4.0</version>
</dependency>

Spring Boot는 기본적으로 Jackson을 사용합니다.

리소스를 XML 형식으로 직렬화하려면 Jackson XML 확장 ( jackson-dataformat-xml )을 의존성에 추가하거나 JAXB 구현 (기본적으로 JDK에서 제공됨)에 다음을 사용하여 폴백해야합니다.  리소스의 @XmlRootElement 어노테이션.

8. 결론

이 튜토리얼에서는 Spring 및 Java 기반 구성을 사용하여 REST 서비스를 구현하고 구성하는 방법을 설명했습니다.

시리즈의 다음 기사 에서는 API의 검색 가능성 , 고급 콘텐츠 협상 및 리소스의 추가 표현 작업 에 중점을 둘 것 입니다.

이 기사의 모든 코드는 Github에서 사용할 수 있습니다 . 이것은 Maven 기반 프로젝트이므로 그대로 가져 와서 실행할 수 있어야합니다.