HTTP Message Converterは、Bodyに送信されたJSON/XMLデータの処理に用いられる



この文章の目的は?


マスターに渡されたJSON/XMLデータをオブジェクトに変換する方法

HTTP Message Converter?


リクエスト本文からメッセージを読み込むか、レスポンス本文にメッセージを作成します.すなわち、@RequestBody, @ResponseBodyの音声を使用する場合、要求/応答を処理する変換器である.
この設定は、依存性に応じて条件付きで登録され、WebMvcConfigurationSupportクラスのコードで見つけることができます.
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {

	// (생략...)

	static {
        ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
        romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
        jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
        jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
        jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
        jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
        jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
        gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
        jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
        kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader);
    }
    
	protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        messageConverters.add(new ByteArrayHttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter());
        messageConverters.add(new ResourceHttpMessageConverter());
        messageConverters.add(new ResourceRegionHttpMessageConverter());
        if (!shouldIgnoreXml) {
            try {
                messageConverters.add(new SourceHttpMessageConverter());
            } catch (Throwable var3) {
            }
        }

        messageConverters.add(new AllEncompassingFormHttpMessageConverter());
        if (romePresent) {
            messageConverters.add(new AtomFeedHttpMessageConverter());
            messageConverters.add(new RssChannelHttpMessageConverter());
        }

        Jackson2ObjectMapperBuilder builder;
        if (!shouldIgnoreXml) {
            if (jackson2XmlPresent) {
                builder = Jackson2ObjectMapperBuilder.xml();
                if (this.applicationContext != null) {
                    builder.applicationContext(this.applicationContext);
                }

                messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
            } else if (jaxb2Present) {
                messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
            }
        }

        if (kotlinSerializationJsonPresent) {
            messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
        }

        if (jackson2Present) {
            builder = Jackson2ObjectMapperBuilder.json();
            if (this.applicationContext != null) {
                builder.applicationContext(this.applicationContext);
            }

            messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        } else if (gsonPresent) {
            messageConverters.add(new GsonHttpMessageConverter());
        } else if (jsonbPresent) {
            messageConverters.add(new JsonbHttpMessageConverter());
        }

        if (jackson2SmilePresent) {
            builder = Jackson2ObjectMapperBuilder.smile();
            if (this.applicationContext != null) {
                builder.applicationContext(this.applicationContext);
            }

            messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
        }

        if (jackson2CborPresent) {
            builder = Jackson2ObjectMapperBuilder.cbor();
            if (this.applicationContext != null) {
                builder.applicationContext(this.applicationContext);
            }

            messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
        }

    }
    
	// (생략...)
    
}
HTTP Message Converterが理解しなければならないのは、Spring Bootの機能ではなく、Springフレームワーク自体の機能です.Spring Bootにおいてのみ,基本的にJacksonJSON 2は依存性を持つ.(スプリングガイドを使用しない場合は、JSONライブラリを依存項目に直接追加する必要があります.)
基本的に多くのHTTPメッセージ変換器が提供されており、追加作業はほとんどありません.😆 ただし、新しいConverterを追加する場合は、WebConfiguratorインタフェースを使用して登録するか、依存性を簡単に追加するだけで登録できます.△講座では、白奇仙が依存性を加えた登録方法を推薦した.
WebConfiguratorインタフェースに登録する場合、2つの方法を追加できます.configureMessageConverters()メソッドは、デフォルト登録されているすべてのConverterを無視するため、extendMessageConverters()メソッドを使用してデフォルト登録されているConverterに追加登録するとより安全です.
@Configuration
public class WebConfig implements WebMvcConfigurer {

//    @Override
//    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//        // 추가할 경우에는 기본적으로 제공하는 메시지 컨버터를 사용할 수 없음
//    }


//    @Override
//    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//        // 기본적으로 제공하는 메시지 컨버터에 추가
//    }
}
@RestController
public class SimpleController {
    @GetMapping("/message")
    public String messageString(@RequestBody String body) {
        return "message " + body;
    }
}
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SimpleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void messageString() throws Exception {
        this.mockMvc.perform(get("/message").content("body"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string("message body"));
    }
}

JSON


Spring Bootでは、基本的にJSON依存性が存在するため、依存性を追加せずに確認することができる.
@RestController
public class SimpleController {
    @GetMapping("/jsonmessage")
    public Person jsonmessage(@RequestBody Person person) {
        return person;
    }
}
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SimpleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;

    @Test
    public void jsonmessage() throws Exception {
        Person person = new Person();
        person.setId(10L);
        person.setName("kevin");
		
        // 객체를 JSON으로 변환
        String jsonString = objectMapper.writeValueAsString(person);

        this.mockMvc.perform(get("/jsonmessage")
                        .contentType(MediaType.APPLICATION_JSON) // JSON 요청을 보냄
                        .accept(MediaType.APPLICATION_JSON) // JSON 응답을 기대
                        .content(jsonString))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(10L))
                .andExpect(jsonPath("$.name").value("kevin"));
    }
}
テストコードで使用されるJSON Path構文は、次のリンクを参照して記述できます.
  • http://jsonpath.com/
  • https://github.com/json-path/JsonPath
  • XML


    JSONと異なりSpring Bootは基本的にXML依存性を追加しないため、単独で追加する必要がある.
    OXM(Object-XML Mapper)ライブラリでは、Springがサポートする依存性にはJacksonXMLとJAXBが含まれているため、JAXBを追加できます.
    <dependency>
    	<groupId>javax.xml.bind</groupId>
    	<artifactId>jaxb-api</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.glassfish.jaxb</groupId>
    	<artifactId>jaxb-runtime</artifactId>
    </dependency>
    <!-- xml을 객체로 변환(Marshalling) / 객체를 xml로 변환(UnMarshalling) -->
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-oxm</artifactId>
    	<version>${spring-framework.version}</version>
    </dependency>
    次に、Marshallerを空に登録し、XMLに変換するクラスに@XmlRootElementフレーズを追加します.
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Bean
        public Jaxb2Marshaller jaxb2Marshaller() {
            Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
            jaxb2Marshaller.setPackagesToScan(Person.class.getPackageName()); // @XmlRootElement 어노테이션 스캔
            return jaxb2Marshaller;
        }
    }
    @XmlRootElement
    @Getter
    @Setter
    @Entity
    public class Person {
    
        @Id @GeneratedValue
        private Long id;
    
        private String name;
    }
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class SimpleControllerTest {
    
        @Autowired
        MockMvc mockMvc;
    
        @Autowired
        Marshaller marshaller; // WebConfig에 등록한 빈을 주입 받음
    
        @Test
        public void xmlmessage() throws Exception {
            Person person = new Person();
            person.setId(10L);
            person.setName("kevin");
    		
            // 객체를 XML로 변환
            StringWriter stringWriter = new StringWriter();
            Result result = new StreamResult(stringWriter);
            marshaller.marshal(person, result);
    
            String xmlString = stringWriter.toString();
    
            this.mockMvc.perform(get("/jsonmessage")
                            .contentType(MediaType.APPLICATION_XML) // XML 요청을 보냄
                            .accept(MediaType.APPLICATION_XML) // XML 응답을 기대
                            .content(xmlString))
                    .andDo(print())
                    .andExpect(status().isOk())
                    .andExpect(xpath("person/id").string("10"))
                    .andExpect(xpath("person/name").string("kevin"));
        }
    }
    テストコードで使用されるXML Path構文は、次のリンクを参照して記述できます.
  • https://www.freeformatter.com/xpath-tester.html
  • https://www.w3schools.com/xml/xpath_syntax.asp
  • 本文の参考

  • 白奇仙のSpringWeb MVC