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

Spring Boot SOAP客户端MTOM附件Content-ID编码不匹配问题咨询

问题根因判断

是的,该编码差异就是服务端报错的直接原因。
虽然URI规范中@属于需要编码的保留字符,转换为%40符合URI编码规则,但绝大多数SOAP服务端的XOP实现不会对cid:开头的href属性做URI解码操作,会直接拿编码后的字符串去匹配多部分请求里的Content-ID原始值,匹配失败就判定二进制附件未上传。

解决方案(针对Spring Boot 2.5.5 + Spring-WS技术栈)

方案一:切换为Axiom消息工厂(推荐)

Spring-WS默认使用的SAAJ消息工厂会强制对所有URI属性做编码,而Axiom消息工厂对XOP的处理更灵活,不会对cid值里的@做强制编码,是最简便的解决方案。

  1. 首先引入Axiom依赖(pom.xml):
<dependency>
    <groupId>org.apache.ws.commons.axiom</groupId>
    <artifactId>axiom-api</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.ws.commons.axiom</groupId>
    <artifactId>axiom-impl</artifactId>
    <version>1.4.0</version>
</dependency>
  1. 替换SOAP消息工厂配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
import org.springframework.ws.client.core.WebServiceTemplate;

@Configuration
public class SoapClientConfig {

    @Bean
    public AxiomSoapMessageFactory soapMessageFactory() {
        AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
        messageFactory.setPayloadCaching(false);
        // 开启XOP支持
        messageFactory.setXopSupportEnabled(true);
        return messageFactory;
    }

    @Bean
    public WebServiceTemplate webServiceTemplate(AxiomSoapMessageFactory messageFactory) {
        WebServiceTemplate template = new WebServiceTemplate(messageFactory);
        // 补充你原有WebServiceTemplate的其他配置:默认接口地址、Marshaller、Unmarshaller等
        return template;
    }
}

方案二:自定义请求拦截器修正href编码

如果受项目约束不能切换Axiom,可以通过客户端拦截器在请求发送前,手动把XOP Include节点href里的%40替换回@:

  1. 实现自定义拦截器:
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

public class XopHrefFixInterceptor implements ClientInterceptor {

    @Override
    public boolean handleRequest(MessageContext messageContext) throws Exception {
        SaajSoapMessage soapMessage = (SaajSoapMessage) messageContext.getRequest();
        XPath xpath = XPathFactory.newInstance().newXPath();
        // 匹配所有xop:Include节点
        NodeList xopIncludes = (NodeList) xpath.evaluate(
            "//*[local-name()='Include' and namespace-uri()='http://www.w3.org/2004/08/xop/include']", 
            soapMessage.getDocument(), 
            XPathConstants.NODESET
        );
        for (int i = 0; i < xopIncludes.getLength(); i++) {
            Element includeNode = (Element) xopIncludes.item(i);
            String href = includeNode.getAttribute("href");
            if (href.startsWith("cid:") && href.contains("%40")) {
                includeNode.setAttribute("href", href.replace("%40", "@"));
            }
        }
        return true;
    }

    @Override
    public boolean handleResponse(MessageContext messageContext) {
        return true;
    }

    @Override
    public boolean handleFault(MessageContext messageContext) {
        return true;
    }

    @Override
    public void afterCompletion(MessageContext messageContext, Exception ex) {
    }
}
  1. 把拦截器注册到WebServiceTemplate:
@Bean
public WebServiceTemplate webServiceTemplate(SaajSoapMessageFactory messageFactory) {
    WebServiceTemplate template = new WebServiceTemplate(messageFactory);
    // 注册自定义拦截器
    template.setInterceptors(new ClientInterceptor[]{new XopHrefFixInterceptor()});
    // 其他原有配置
    return template;
}

方案三:自定义Content-ID生成规则

也可以直接修改附件的Content-ID生成逻辑,避免使用@字符,从根源上消除编码差异。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 00:36:03