Spring Boot集成GUS BIR测试SOAP API时登录请求返回400错误求助
看起来你在集成GUS BIR测试环境的SOAP API时卡在了登录环节,400错误一般是请求格式不符合服务端要求,而且你用Postman能成功,说明问题肯定出在Spring WS的请求构造上,我帮你梳理几个大概率的问题点:
1. 手动构造XML与JAXB Marshaller的冲突(核心问题)
你现在的代码犯了一个关键错误:同时手动构造XML字符串,又配置了JAXB Marshaller来序列化请求对象。
看你的login方法,你手动拼好了完整的SOAP Envelope XML,然后把它塞进SoapRequest对象里传给webServiceTemplate.marshalSendAndReceive。但你配置的Jaxb2Marshaller会把这个SoapRequest对象序列化,最终发送给GUS的不是你拼的XML,而是SoapRequest对象转成的XML(比如如果SoapRequest有个requestXml字段,那发送的会是<soapRequest><requestXml>你拼的XML转义后的内容</requestXml></soapRequest>),这和服务端预期的SOAP结构完全不符,自然返回400。
解决方法:二选一
选项A:放弃手动XML,用标准JAXB类(推荐)
GUS的API有官方WSDL,你应该用wsimport工具从WSDL生成JAXB类,而不是自己定义SoapRequest、SoapResponse:
- 执行命令生成类(替换路径为你自己的项目目录):
wsimport -s ./src/main/java -p com.yourpackage.gus https://wyszukiwarkaregontest.stat.gov.pl/wsBIR/UslugaBIRzewnPubl.svc?wsdl
- 用生成的类构造请求,比如
Zaloguj对象,然后直接传给webServiceTemplate.marshalSendAndReceive,Marshaller会自动生成正确的SOAP结构。
选项B:直接发送手动构造的XML(快速验证)
如果想先快速验证,可以跳过JAXB Marshaller,直接把你拼好的XML发送出去:
修改login方法里的请求发送逻辑:
try { // 直接发送原始XML webServiceTemplate.send(LOGIN_URL, new WebServiceMessageCallback() { @Override public void doWithMessage(WebServiceMessage message) throws IOException { SaajSoapMessage soapMessage = (SaajSoapMessage) message; // 配置SOAP 1.2的Content-Type,必须带action参数 MimeHeaders headers = soapMessage.getSaajMessage().getMimeHeaders(); headers.addHeader("Content-Type", "application/soap+xml; charset=UTF-8; action=\"http://CIS/BIR/PUBL/2014/07/IUslugaBIRzewnPubl/Zaloguj\""); // 写入手动构造的XML Writer writer = message.getPayloadResult().getWriter(); writer.write(requestXml); writer.flush(); } }); // 这里之后需要解析响应,建议用WSDL生成的类来解析,或者手动解析XML } catch (Exception ex) { System.err.println("❌ Błąd logowania do GUS: " + ex.getMessage()); throw new RuntimeException("Błąd logowania do GUS.", ex); }
同时,暂时注释掉WebServiceTemplate的Marshaller配置,避免冲突:
// this.webServiceTemplate.setMarshaller(marshaller); // this.webServiceTemplate.setUnmarshaller(marshaller);
2. SOAP 1.2的Content-Type头配置错误
GUS用的是SOAP 1.2,对于SOAP 1.2,SOAP Action必须通过Content-Type头的action参数传递,而不是单独的SOAPAction头。
你现在的CustomSoapActionCallback里只设置了Content-Type: application/soap+xml; charset=UTF-8,没有带action参数,这不符合SOAP 1.2的规范。即使你后来在login里传了空的SOAP Action,也是错误的。
正确的Content-Type格式应该是:
application/soap+xml; charset=UTF-8; action="http://CIS/BIR/PUBL/2014/07/IUslugaBIRzewnPubl/Zaloguj"
如果选择保留JAXB的方式,需要修改CustomSoapActionCallback的doWithMessage方法:
@Override public void doWithMessage(org.springframework.ws.WebServiceMessage message) throws IOException { SaajSoapMessage soapMessage = (SaajSoapMessage) message; // 对于SOAP 1.2,setSoapAction可能不会自动处理Content-Type,所以手动构造 MimeHeaders headers = soapMessage.getSaajMessage().getMimeHeaders(); // 清除原有Content-Type,避免重复 headers.removeHeader("Content-Type"); headers.addHeader("Content-Type", "application/soap+xml; charset=UTF-8; action=\"" + soapAction + "\""); }
同时,在login方法里调用时,传入正确的SOAP Action:
new CustomSoapActionCallback("http://CIS/BIR/PUBL/2014/07/IUslugaBIRzewnPubl/Zaloguj")
3. 自定义JAXB类与GUS API结构不匹配
你自己定义的SoapRequest、SoapResponse类,可能和GUS API的实际请求/响应结构不匹配,这会导致Marshaller生成的XML不符合要求,或者Unmarshaller解析响应失败。
最稳妥的方式是从GUS的官方WSDL生成JAXB类,这样类的结构完全匹配服务端的要求,不会出现结构不兼容的问题。生成命令我在第1点里已经写了,你可以执行一下。
4. 额外检查点
- 确认你的
API_KEY是正确的,Postman里能成功用这个Key登录,说明是对的,但还是要再核对一遍; - 检查
LOGIN_URL是否正确,测试环境的URL是https://wyszukiwarkaregontest.stat.gov.pl/wsBIR/UslugaBIRzewnPubl.svc,这个是对的; - 检查WebServiceTemplate的MessageSender配置,连接超时和读取超时是否合理,你现在的配置是5秒连接,10秒读取,这个没问题。
建议你先解决第1点的冲突问题,这是最可能导致400错误的原因,然后再调整Content-Type头的配置,应该就能解决问题了。如果还有问题,可以把发送的实际XML日志打出来(用Spring WS的日志功能,或者在发送前输出),和Postman里的正确请求对比,就能快速找到差异。
备注:内容来源于stack exchange,提问作者Mateusz Starosta

