You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

引入Azure Service Bus的Spring Cloud Stream Binder致测试失败

问题:引入Azure Stream Binder依赖后RestTemplate测试部分失败

添加以下Maven依赖后:

<dependency>
    <groupId>com.azure.spring</groupId>
    <artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId>
</dependency>

部分RestTemplate客户端测试突然失败,错误分为两类:

错误1:JSON断言失败

java.lang.AssertionError: JSON path "$"
Expected: (a collection containing <152> and a collection containing <153>)
     but: a collection containing <152> was String "<UnmodifiableRandomAccessList><item>152</item><item>153</item></UnmodifiableRandomAccessList>"

错误2:请求Content-Type不符

Expected :application/json
Actual   :application/xml;charset=UTF-8

测试代码示例:

@Autowired
private RestTemplate restTemplate;

private MockRestServiceServer mockServer;

@BeforeEach
public void init()
{
    mockServer = MockRestServiceServer.createServer(restTemplate);
}

public void test() throws Exception
{
    mockServer.expect(requestTo(url))
        .andExpect(method(HttpMethod.POST))
        .andExpect(jsonPath("$", hasItems(152, 153)))
        .andRespond(withStatus(HttpStatus.OK)
        .contentType(MediaType.APPLICATION_JSON)
        .body("{}")); // actual code removed for brevity
    
    ResultDTO result = client.makeRestCall(Set.of(1L)); // 内部通过RestTemplate调用目标URL

    assertNotNull(result);
}

奇怪的是测试类中仅4/8个测试失败,无明显规律,移除该Azure依赖后所有测试恢复正常。


原因分析

问题的核心是Azure Stream Binder依赖间接引入了XML消息转换器,打乱了RestTemplate的消息转换优先级:

  • 该依赖会自动引入jackson-dataformat-xml依赖,导致Spring自动注册MappingJackson2XmlHttpMessageConverter到RestTemplate的转换器列表中
  • 当RestTemplate没有显式配置转换器顺序时,XML转换器的优先级可能高于JSON转换器
  • 这会引发两个问题:
    1. 发送请求时,原本要序列化为JSON的集合被转换成了XML格式(即错误中的<UnmodifiableRandomAccessList>标签内容)
    2. 请求的Content-Type被自动设置为application/xml,与测试预期的application/json不符
  • 部分测试正常的原因是这些测试可能显式指定了请求Content-Type,或者在特定场景下RestTemplate优先匹配到了JSON转换器

解决方法

1. 显式配置RestTemplate的转换器顺序

在配置RestTemplate Bean时,确保JSON转换器排在XML转换器之前:

@Bean
public RestTemplate restTemplate(ObjectMapper objectMapper) {
    RestTemplate restTemplate = new RestTemplate();
    List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
    
    // 移除已有的XML转换器(如果存在)
    converters.removeIf(converter -> converter instanceof MappingJackson2XmlHttpMessageConverter);
    
    // 将JSON转换器放到最前面,确保优先使用
    MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(objectMapper);
    converters.add(0, jsonConverter);
    
    // 若业务需要XML转换,再将XML转换器添加到列表末尾
    converters.add(new MappingJackson2XmlHttpMessageConverter());
    
    return restTemplate;
}

2. 排除Azure依赖中的XML转换包

在Maven依赖中排除jackson-dataformat-xml,避免自动注册XML转换器:

<dependency>
    <groupId>com.azure.spring</groupId>
    <artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </exclusion>
    </exclusions>
</dependency>

3. 在请求中显式指定Content-Type

发送请求时明确设置请求头为application/json,强制RestTemplate使用JSON转换器:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Set<Long>> requestEntity = new HttpEntity<>(Set.of(1L), headers);
ResultDTO result = restTemplate.postForObject(url, requestEntity, ResultDTO.class);

内容的提问来源于stack exchange,提问作者Daniel Pop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 16:27:44