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

如何使用OpenFeign将Java 8 SOAP调用升级至Java 11

嘿,我刚好处理过类似的 Java 11 迁移 + OpenFeign 调用 SOAP 1.2 服务的场景,你的 400 错误其实是两个核心问题导致的,咱们一步步拆解解决:

核心问题分析

1. SOAP 信封命名空间不匹配

你提到生成的信封用了 http://schemas.xmlsoap.org/soap/envelope/,这是SOAP 1.1 的命名空间,但目标服务是 SOAP 1.2,对应的标准命名空间应该是 http://www.w3.org/2003/05/soap-envelope。服务端会严格校验信封的命名空间,不匹配直接返回 400。

2. 缺少 WS-Addressing(WSA)规范的 Header

目标服务要求的 wsa 头(比如 wsa:To、wsa:Action、wsa:MessageID)是 SOAP 服务的标准寻址字段,OpenFeign 默认不会自动生成这些内容,必须手动封装到 SOAP 信封的 Header 部分。


具体修复方案

步骤1:定义符合 SOAP 1.2 规范的信封实体

先手动封装 SOAP 1.2 的信封结构,包含 WSA Header 和请求体:

import jakarta.xml.bind.annotation.*;

// SOAP 1.2 信封根节点
@XmlRootElement(name = "Envelope", namespace = "http://www.w3.org/2003/05/soap-envelope")
@XmlNamespace(prefix = "soap", namespaceURI = "http://www.w3.org/2003/05/soap-envelope")
public class Soap12Envelope<T> {
    @XmlElement(name = "Header", namespace = "http://www.w3.org/2003/05/soap-envelope")
    private SoapWsaHeader header;

    @XmlElement(name = "Body", namespace = "http://www.w3.org/2003/05/soap-envelope")
    private T body;

    // 构造器、Getter/Setter
    public Soap12Envelope() {}
    public Soap12Envelope(SoapWsaHeader header, T body) {
        this.header = header;
        this.body = body;
    }
}

// WSA Header 实体
@XmlRootElement(name = "Header", namespace = "http://www.w3.org/2005/08/addressing")
@XmlNamespace(prefix = "wsa", namespaceURI = "http://www.w3.org/2005/08/addressing")
public class SoapWsaHeader {
    @XmlElement(name = "To", namespace = "http://www.w3.org/2005/08/addressing")
    private String to;

    @XmlElement(name = "Action", namespace = "http://www.w3.org/2005/08/addressing")
    private String action;

    @XmlElement(name = "MessageID", namespace = "http://www.w3.org/2005/08/addressing")
    private String messageId;

    // 构造器、Getter/Setter
    public SoapWsaHeader(String to, String action, String messageId) {
        this.to = to;
        this.action = action;
        this.messageId = messageId;
    }
}

步骤2:配置 Feign 接口与编码器

更新 Feign 接口,指定正确的 Content-Type,并使用自定义的 SOAP 1.2 信封作为请求体:

import feign.Headers;
import feign.RequestLine;

@Headers({
    "Content-Type: application/soap+xml;charset=UTF-8",
    "Accept: application/xop+xml;charset=UTF-8"
})
public interface ZalogujSoapClient {
    @RequestLine("POST /your-target-soap-endpoint")
    String sendZalogujRequest(Soap12Envelope<Zaloguj> request);
}

如果是 Spring Cloud OpenFeign,需要配置自定义 JAXB 编码器,确保序列化时使用 SOAP 1.2 的命名空间和编码:

import feign.codec.Encoder;
import feign.jaxb.JAXBContextFactory;
import feign.jaxb.JaxbEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignSoapConfig {
    @Bean
    public Encoder feignSoapEncoder() {
        JAXBContextFactory contextFactory = new JAXBContextFactory.Builder()
                .withMarshallerJAXBEncoding("UTF-8")
                .withMarshallerSchemaLocation(
                    "http://www.w3.org/2003/05/soap-envelope",
                    "http://www.w3.org/TR/2003/REC-soap12-part1-20030624/soap12-part1.xsd"
                )
                .build();
        return new JaxbEncoder(contextFactory);
    }
}

步骤3:构造包含 WSA 头的请求并调用

在业务代码中,组装完整的 SOAP 请求:

import java.util.UUID;

// 构造 WSA 头(参数要和服务端定义的完全匹配)
SoapWsaHeader wsaHeader = new SoapWsaHeader(
    "http://your-target-service-url/endpoint", // wsa:To 目标服务地址
    "http://your-service-namespace/Zaloguj",   // wsa:Action 服务操作的命名空间
    "urn:uuid:" + UUID.randomUUID().toString() // wsa:MessageID 唯一请求标识
);

// 构造业务请求体
Zaloguj zalogujBody = new Zaloguj();
zalogujBody.setUsername("your-username");
zalogujBody.setPassword("your-password");
// 其他参数设置...

// 封装成 SOAP 1.2 信封
Soap12Envelope<Zaloguj> soapRequest = new Soap12Envelope<>(wsaHeader, zalogujBody);

// 调用 Feign 客户端
String soapResponse = zalogujSoapClient.sendZalogujRequest(soapRequest);

步骤4:处理 MTOM 格式的响应

服务端返回的 application/xop+xml 是 MTOM 格式(用于优化二进制数据传输),OpenFeign 默认解码器无法解析,需要自定义解码器支持 MTOM:

import feign.codec.Decoder;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.transform.stream.StreamSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.InputStream;

@Configuration
public class FeignMtomDecoderConfig {
    @Bean
    public Decoder feignMtomDecoder() {
        return (response, type) -> {
            try (InputStream inputStream = response.body().asInputStream()) {
                JAXBContext jaxbContext = JAXBContext.newInstance(YourResponseEntity.class);
                Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
                // 开启 MTOM 支持(依赖 JAXB 实现,比如 Metro)
                unmarshaller.setProperty("com.sun.xml.bind.mtom.enabled", true);
                return unmarshaller.unmarshal(new StreamSource(inputStream));
            } catch (Exception e) {
                throw new RuntimeException("解析 MTOM 响应失败", e);
            }
        };
    }
}

调试建议

开启 Feign 全量日志,确认生成的 SOAP 请求是否符合要求:

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignLogConfig {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

日志会打印完整的请求 XML,你可以直接检查命名空间和 WSA 头是否正确。

内容的提问来源于stack exchange,提问作者Piotr Żak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 14:27:59