【问题标题】:Spring Boot - postForEntity(): what is content type of the response?Spring Boot - postForEntity():响应的内容类型是什么?
【发布时间】:2020-02-07 20:23:55
【问题描述】:

我正在尝试在一组通过 HTTP REST 通信的 Spring Boot 微服务上设置和运行集成测试。我正在使用 Citrus 框架作为集成测试框架。

我有一个测试场景,其中涉及一个“主”服务调用另外两个服务来完成工作。我的测试有启动进程和“模拟”工作服务的调用。我将在下面包含源代码。

我遇到了一个问题,我收到一个异常,似乎表明测试期望接收的消息(如application/json)正在以text/plain 的形式通过,并且找不到要使用的消息转换器.奇怪的是,正在接收的消息应该是 JSON(或者至少看起来像 JSON)。

我在发送端(POSTer)遇到了类似的问题,Citrus 在接收消息时遇到了问题。我追查到我没有设置任何 HTTP 标头,特别是 AcceptContent-Type。一旦我适当地设置了这些,Citrus 就会对它收到的东西感到满意。

服务代码:

HttpEntity<GenerateRouteCommand> entity =
    (HttpEntity<GenerateRouteCommand>) HttpEntityBuilder.createHttpEntity(...);
ResponseEntity<GenerateRouteStatus> response =
    rgTemplate.postForEntity(url, entity, GenerateRouteStatus.class);

例外:

org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class edu.mit.ll.mission_services.messages.GenerateRouteStatus] and content type [text/plain;charset=utf-8]                                                                                             
        at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:121)                                              
        at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:994)                                                
        at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:977)                                                
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:737)                                                                                  
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)                                                                                    
        at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:445)                                                                              
        at edu.mit.ll.mission_services.service.mission_planner.service.MissionPlanner.postGenerateRoute(MissionPlanner.java:214)                                         
        at edu.mit.ll.mission_services.service.mission_planner.service.MissionPlanner.planMission(MissionPlanner.java:144)                                               
        at edu.mit.ll.mission_services.service.mission_planner.controller.MissionPlannerController$Runner.executeTask(MissionPlannerController.java:51)                  
        at edu.mit.ll.mission_services.common.util.ITask.run(ITask.java:37)                                                                                              
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)                                                                               
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)                                                                               
        at java.lang.Thread.run(Thread.java:748)

在 POST 请求中发送的实体将 AcceptContent-Type 标头设置为 application/json。我想我在那里还可以。上面的堆栈跟踪似乎是说响应消息没有设置标头(或设置为错误的值),并且由于测试用例需要 JSON,因此测试失败。

当这些服务“正常”运行(即不通过 Citrus 驱动)时,一切正常。我没有发现任何问题。

【问题讨论】:

  • 你可以调试:ResponseEntity&lt;String&gt; response = rgTemplate.postForEntity(url, entity, String.class); 看看你到底收到了什么
  • 有用的提示。我用它来验证是否返回了 JSON 字符串,但实际上,内容类型是 text/plain

标签: java spring-boot spring-rest


【解决方案1】:

我最近在尝试解码 REST 响应时两次遇到了这个问题。我通过在RestTemplate 上显式设置HttpMessageConverters 解决了这个问题。

如果我是你,我会调试并查看响应内容的实际样子。如果是 JSON,请尝试使用 GsonHttpMessageConverter,或者如果它真的只是文本,请尝试使用 StringHttpMessageConverter。或者通过以下方式将两者都添加到RestTemplate

private final RestTemplate restTemplate = new RestTemplateBuilder()
        .messageConverters(Lists.newArrayList(new GsonHttpMessageConverter(GsonHelper.getInstance()), new StringHttpMessageConverter()))
        .build();

如果这些转换器似乎都不能解决您的异常,请查看HttpMessageConverter 接口的所有不同实现,并推断哪一个适合您的特定情况。有相当多的实现实现了这个接口。

【讨论】:

  • 我查看了我的 REST 模板工厂并确认我正在添加以下转换器:MappingJackson2HttpMessageConverterStringHttpMessageConverter。这段代码已经到位。
  • 我认为这是 Citrus 框架的问题(或者可能是我使用它的方式)。我做了一些实验,验证了当服务正常运行时,内容类型如预期的那样是application/json。当我尝试运行 Citrus 集成测试时,内容类型为 text/plain。我需要弄清楚原因。
【解决方案2】:

弄清楚我需要做些什么来为从 Citrus 中的模拟服务器返回的消息设置预期的内容类型 application/json。我错过了以下声明:

.contentType(ContentType.APPLICATION_JSON.getMimeType())

一些上下文:

// Set route assessor to return response.
runner.http(builder -> builder
    .server(raServer)
    .send()
    .response(HttpStatus.OK)
    .messageType(MessageType.JSON)
    .contentType(ContentType.APPLICATION_JSON.getMimeType())
    .payload(new ClassPathResource("templates/assess-route-status.json")));

显然,如果未指定,则默认为text/plain

【讨论】:

    猜你喜欢
    • 2013-07-29
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    • 2018-12-01
    • 1970-01-01
    相关资源
    最近更新 更多