ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] REST API 만들기(12) - Content Negotiation 설정
    Spring Boot/2.4.x - REST API 만들기 2020. 5. 20. 17:46
    반응형

    REST에서는 하나의 리소스에 대해서 여러 형태의 Representation을 가질 수 있습니다. 어떤 요청을 처리할 때 응답을 application/json 형태로 할 수도 있고, application/xml 형태로 할 수도 있습니다. 클라이언트가 요청을 전달할 때 HTTP Header 중에서 Accept라는 이름을 이용해서 원하는 응답 형태를 명시하면, 서버에서는 클라이언트가 원하는 형태로 결과를 전달합니다. 이러한 처리 과정을 Content Negotiation이라고 합니다.

     

    1. WebMvcConfig.java 수정

    WebMvcConfigurer의 configureContentNegotiation를 이용해서 미디어 타입을 설정할 수 있습니다.

    WebMvcConfig.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    package com.api.board.config;
     
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
    import org.springframework.oxm.jaxb.Jaxb2Marshaller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    import com.api.board.interceptor.BoardInterceptor;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
     
    @ComponentScan(basePackages = {"com.api.board.controller"}, useDefaultFilters = false, includeFilters = {@Filter(Controller.class)})
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
     
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new BoardInterceptor())
                    .addPathPatterns("/**")
                    .excludePathPatterns("/sample/**");
        }
        
        @Bean
        public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
     
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            
            ObjectMapper objectMapper = converter.getObjectMapper();
            objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
            objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
     
            JaxbAnnotationModule module = new JaxbAnnotationModule();
            objectMapper.registerModule(module);
     
            return converter;
        }
        
        @Bean
        public MarshallingHttpMessageConverter marshallingHttpMessageConverter() {
            MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter();
            converter.setMarshaller(jaxb2Marshaller());
            converter.setUnmarshaller(jaxb2Marshaller());
            return converter;
        }
        
        @Bean
        public Jaxb2Marshaller jaxb2Marshaller() {
            Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
            marshaller.setPackagesToScan(new String[] { "com.api.board.domain" });
            return marshaller;
        }
        
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            configurer.favorParameter(true)
                      .ignoreAcceptHeader(false)
                      .defaultContentType(MediaType.APPLICATION_JSON)
                      .mediaType("json", MediaType.APPLICATION_JSON)
                      .mediaType("xml", MediaType.APPLICATION_XML);
        }
    }
    cs

    요청을 처리한 후 적절한 응답 형태를 선택하기 위해서 클라이언트로부터 수신된 요청의 미디어 타입을 가지고 결정하는데, 그 과정은 다음과 같습니다.

    1. favorParameter 속성 값이 true(기본값 false)이고, 요청 파라미터에 미디어 타입을 정의하는 값이 포함되어 있다면 그 값을 미디어 타입으로 사용합니다. 파라미터의 변수명은 'format' 입니다.

    (예 : http://localhost:8080/board?format=xml, http://localhost:8080/board?format=json)

    2. 미디어 타입을 찾지 못한 경우 ignoreAcceptHeader의 속성 값이 false(기본값 false)이면 HTTP Header 값의 Accept를 미디어 타입으로 사용합니다.

    3. 미디어 타입을 찾지 못한 경우 ContentNegotiationConfigurer에 defaultContentType 속성 값이 정의되어 있다면 그 값을 미디어 타입으로 사용합니다.

     

    소스 코드는 Github Repository - https://github.com/tychejin1218/api-board_v1 (branch : section12) 를 참조하세요.

    Github에서 프로젝트 가져오기 - https://tychejin.tistory.com/33

    반응형

    댓글

Designed by Tistory.