【问题标题】:spring test rest template : Could not extract response: no suitable HttpMessageConverter found for response type春季测试休息模板:无法提取响应:没有找到适合响应类型的 HttpMessageConverter
【发布时间】:2020-02-09 22:48:24
【问题描述】:

我得到了这个异常

org.springframework.web.client.RestClientException:无法提取 响应:没有找到适合响应类型 [AnalyticsResponse] 和内容类型 [application/json;charset=UTF-8] 的 HttpMessageConverter

仅在我的 junit 测试用例中(Rest 端点工作正常)如果我的 DTO 包含 一个以用户定义的类作为键的映射

@Data
public class AnalyticsResponse {

    private List<Committer> commitersList; //OK
    private Map<Committer , Long> comittersCommitsMap; // Problem
    private Map<Date, List<CommitItem>> commitItemsTimeLineMap; //OK

}

如果 committersCommitsMap 字段被移除,一切都会正常

我的测试用例代码sn-p:

ResponseEntity<AnalyticsResponse> analyticsResponse = testRestTemplate.getForEntity(ANALYSIS_CONTROLLER_BASE_URL+"analytics?repo-full-name=" + searchResponse.getBody().get(0).getFull_name() ,
                AnalyticsResponse.class); 

---更新:提交者类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Committer {

    private  String name;
    private String email;

}

【问题讨论】:

    标签: spring spring-mvc spring-test fasterxml


    【解决方案1】:

    默认情况下,ObjectMapper 无法确定Map 的序列化和反序列化,其中key 不是String,您必须提供自定义实现。

    CommitterDate实现KeyDeserializerJsonSerializer(作为Map的键)

    使用KeyDeserializerKeySerializerJsonSerializer)配置ObjectMapper - register 模块

    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        // KeyDeserializer for Committer (simple without 'null' check)
        @RequiredArgsConstructor
        public static class CommitterKeyDeserializer extends KeyDeserializer {
    
            private final ObjectMapper mapper;
    
            @Override
            public Object deserializeKey(final String key,
                                         final DeserializationContext ctxt)
                    throws IOException, JsonProcessingException {
    
                return mapper.readValue(key, Committer.class);
            }
        }
    
        // KeyDeserializer for Date (simple without 'null' check)
        @RequiredArgsConstructor
        public static class DateKeyDeserializer extends KeyDeserializer {
    
            private final ObjectMapper mapper;
    
            @Override
            public Object deserializeKey(final String key,
                                         final DeserializationContext ctxt)
                    throws IOException, JsonProcessingException {
    
                return mapper.readValue(key, Date.class);
            }
        }
    
        // JsonSerializer for Committer (simple without 'null' check)
        @RequiredArgsConstructor
        public static class CommitterJsonSerializer extends JsonSerializer<Committer> {
    
            private final ObjectMapper mapper;
    
            @Override
            public void serialize(Committer committer,
                                  JsonGenerator jgen,
                                  SerializerProvider provider)
                    throws IOException, JsonProcessingException {
    
                jgen.writeFieldName(mapper.writeValueAsString(committer));
            }
    
        }
    
        // JsonSerializer for Date (simple without 'null' check)
        public static class DateJsonSerializer extends JsonSerializer<Date> {
    
            @Override
            public void serialize(Date date,
                                  JsonGenerator jgen,
                                  SerializerProvider provider)
                    throws IOException, JsonProcessingException {
    
                jgen.writeFieldName(String.valueOf(date.getTime()));
            }
    
        }
    
        // ObjectMapper configuration
        @Bean
        public ObjectMapper objectMapper() {
            ObjectMapper mapper = new ObjectMapper();
    
            // register module with custom serializers and deserializers
            mapper.registerModule(new SimpleModule()
                    .addKeyDeserializer(
                            Committer.class,
                            new CommitterKeyDeserializer(mapper))
                    .addKeyDeserializer(
                            Date.class,
                            new DateKeyDeserializer(mapper))
                    .addKeySerializer(
                            Committer.class,
                            new CommitterJsonSerializer(mapper))
                    .addKeySerializer(
                            Date.class,
                            new DateJsonSerializer()));
    
            return mapper;
        }
    
        // RestTemplate configuration
        @Bean
        public RestTemplate restTemplate(List<HttpMessageConverter<?>> converters) {
            RestTemplate restTemplate = new RestTemplate();
            // add spring's predefined converters
            restTemplate.setMessageConverters(converters);
    
            return restTemplate;
        }
    
    }
    

    请注意,在这个简单的实现中,comittersCommitsMap 中的 key Committer 在 JSON 响应中表示为 StringRestTemplate 与此实现同样有效) :

    {
        "commitersList": [
            {
                "name": "name",
                "email": "email"
            }
        ],
        "comittersCommitsMap": {
            "{\"name\":\"name\",\"email\":\"email\"}": 1
        },
        "commitItemsTimeLineMap": {
            "1570929503854": [
                {
                    "data": "data"
                }
            ]
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-09-03
      • 1970-01-01
      • 2018-08-18
      • 2014-09-28
      • 2017-04-05
      • 2020-02-12
      • 2014-03-18
      • 2021-11-18
      • 1970-01-01
      相关资源
      最近更新 更多