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

SpringBoot调用SOAP服务报400错误:如何正确设置请求Payload?

SpringBoot + Java 11调用SOAP Web服务报400错误排查与解决

问题现象

调用SOAP服务时抛出以下错误:

org.springframework.ws.client.WebServiceTransportException: Bad Request [400]

Postman和SOAP UI均可正常调用该服务(返回200状态码),说明问题出在Java代码ProconSoapClient.java中。

Postman成功请求示例

curl --location --request POST 'http://******/ProCONWS/WebService_PCSOrderEntry.asmx' \
--header 'Content-Type: application/soap+xml;charset=UTF-8;action="http://www.procon.dk/calculate_MD5"' \
--header 'Content-Length: 458' \
--data-raw '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:proc="http://www.procon.dk/">
   <soap:Header/>
   <soap:Body>
      <proc:calculate_MD5>
         <proc:user>*****</proc:user>
         <proc:timestamp>20220915 144942</proc:timestamp>
         <proc:Secret_word>********</proc:Secret_word>
      </proc:calculate_MD5>
   </soap:Body>
</soap:Envelope>'

报错的Java代码(ProconSoapClient.java)

核心疑问:如何正确设置请求体?objectFactory.createString仅接受String类型,但需要发送CalculateMD5对象。

// request headers
Map<String, String> headers = new HashMap<>();
headers.put("Cache-Control", "no-cache");
headers.put("Accept-Encoding", "gzip,deflate");
headers.put("Content-Type", "application/soap+xml;charset=UTF-8;action=\"http://www.procon.dk/calculate_MD5\"");
headers.put("Content-Length", "458");
headers.put("Connection", "Keep-Alive");
//headers.put("charset", "utf-8");
WebServiceMessageCallback callback = getRequestCallback(headers);

// request object
ObjectFactory objectFactory = new ObjectFactory();
CalculateMD5 calculateMD5 = objectFactory.createCalculateMD5();
calculateMD5.setUser(PROCON_SOAP_ACCESS_USER);
calculateMD5.setTimestamp(URLEncoder.encode(DateUtil.dateToString(new Date(), "yyyyMMdd HHmmss"), StandardCharsets.UTF_8));
calculateMD5.setSecretWord(PROCON_SOAP_ACCESS_SECRETWORD);

// payload
final String payload = marshal(calculateMD5);
// todo: what must go here? accepts a String but the object is a CalculateMD5
JAXBElement<String> jaxbElementPayload = objectFactory.createString(payload);

// call the SOAP service
Object response = getWebServiceTemplate().marshalSendAndReceive(PROCON_SOAP_ACCESS_URL, jaxbElementPayload, callback);

生成的类说明

CalculateMD5和ObjectFactory均由WSDL生成。

CalculateMD5.java

/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element name="user" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
 *         &lt;element name="timestamp" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
 *         &lt;element name="Secret_word" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
 *       &lt;/sequence&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "user",
    "timestamp",
    "secretWord"
})
@XmlRootElement(name = "calculate_MD5")
public class CalculateMD5 {

    protected String user;
    protected String timestamp;
    @XmlElement(name = "Secret_word")
    protected String secretWord;

    /**
     * Gets the value of the user property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getUser() {
        return user;
    }

    /**
     * Sets the value of the user property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setUser(String value) {
        this.user = value;
    }

    /**
     * Gets the value of the timestamp property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getTimestamp() {
        return timestamp;
    }

    /**
     * Sets the value of the timestamp property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setTimestamp(String value) {
        this.timestamp = value;
    }

    /**
     * Gets the value of the secretWord property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getSecretWord() {
        return secretWord;
    }

    /**
     * Sets the value of the secretWord property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setSecretWord(String value) {
        this.secretWord = value;
    }

}

ObjectFactory.java

@XmlRegistry
public class ObjectFactory {

    private final static QName _String_QNAME = new QName("http://www.procon.dk/", "string");

    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.clubtravel.powwow.soap.client.procon.generated
     * 
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link CalculateMD5 }
     * 
     */
    public CalculateMD5 createCalculateMD5() {
        return new CalculateMD5();
    }

    /**
     * Create an instance of {@link CalculateMD5Response }
     * 
     */
    public CalculateMD5Response createCalculateMD5Response() {
        return new CalculateMD5Response();
    }

    /**
     * Create an instance of {@link PcsCreateOrder }
     * 
     */
    public PcsCreateOrder createPcsCreateOrder() {
        return new PcsCreateOrder();
    }

    /**
     * Create an instance of {@link PcsCreateOrderResponse }
     * 
     */
    public PcsCreateOrderResponse createPcsCreateOrderResponse() {
        return new PcsCreateOrderResponse();
    }

    /**
     * Create an instance of {@link PcsGetOrderStatus }
     * 
     */
    public PcsGetOrderStatus createPcsGetOrderStatus() {
        return new PcsGetOrderStatus();
    }

    /**
     * Create an instance of {@link PcsGetOrderStatusResponse }
     * 
     */
    public PcsGetOrderStatusResponse createPcsGetOrderStatusResponse() {
        return new PcsGetOrderStatusResponse();
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}
     * 
     * @param value
     *     Java instance representing xml element's value.
     * @return
     *     the new instance of {@link JAXBElement }{@code <}{@link String }{@code >}
     */
    @XmlElementDecl(namespace = "http://www.procon.dk/", name = "string")
    public JAXBElement<String> createString(String value) {
        return new JAXBElement<String>(_String_QNAME, String.class, null, value);
    }

}

问题分析与解决方案

核心错误点

  1. 请求体处理逻辑错误:无需手动将CalculateMD5序列化为字符串再用createString包装,这会导致请求体格式完全不符合SOAP服务预期。Spring WebServiceTemplate支持直接传入JAXB可序列化对象或对应的JAXBElement,框架会自动完成序列化。
  2. Timestamp编码多余:Postman示例中的timestamp为原始字符串(如20220915 144942),但代码中使用URLEncoder.encode会将空格转为+,导致参数不匹配。
  3. 硬编码Content-Length风险:Content-Length应由框架自动计算,硬写固定值会因实际请求体长度变化导致错误。

修正后的代码

// request headers
Map<String, String> headers = new HashMap<>();
headers.put("Cache-Control", "no-cache");
headers.put("Accept-Encoding", "gzip,deflate");
headers.put("Content-Type", "application/soap+xml;charset=UTF-8;action=\"http://www.procon.dk/calculate_MD5\"");
headers.put("Connection", "Keep-Alive");
// 移除硬编码的Content-Length,由框架自动处理
WebServiceMessageCallback callback = getRequestCallback(headers);

// request object
ObjectFactory objectFactory = new ObjectFactory();
CalculateMD5 calculateMD5 = objectFactory.createCalculateMD5();
calculateMD5.setUser(PROCON_SOAP_ACCESS_USER);
// 移除URLEncoder.encode,直接传入原始格式的时间字符串
calculateMD5.setTimestamp(DateUtil.dateToString(new Date(), "yyyyMMdd HHmmss"));
calculateMD5.setSecretWord(PROCON_SOAP_ACCESS_SECRETWORD);

// 直接使用CalculateMD5对象(因该类带有@XmlRootElement注解,WebServiceTemplate可直接处理)
// 若需使用JAXBElement,可手动构建(ObjectFactory未生成对应create方法属于WSDL生成小问题)
JAXBElement<CalculateMD5> jaxbElementPayload = new JAXBElement<>(
    new QName("http://www.procon.dk/", "calculate_MD5"),
    CalculateMD5.class,
    calculateMD5
);

// 调用SOAP服务 - 可直接传入calculateMD5或jaxbElementPayload
Object response = getWebServiceTemplate().marshalSendAndReceive(PROCON_SOAP_ACCESS_URL, calculateMD5, callback);
// 或使用jaxbElementPayload:
// Object response = getWebServiceTemplate().marshalSendAndReceive(PROCON_SOAP_ACCESS_URL, jaxbElementPayload, callback);

额外检查点

  • 确认getRequestCallback方法正确设置所有请求头,未覆盖或遗漏Content-Type中的action参数。
  • 开启Spring WS日志,对比实际发送的SOAP请求与Postman请求内容,确保格式一致。可在application.properties中添加:
    logging.level.org.springframework.ws.client.MessageTracing.sent=TRACE
    logging.level.org.springframework.ws.server.MessageTracing.received=TRACE
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:40:45