Java 服务器无法识别 HTTP 标头“SOAPAction”的值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34633396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
The value of the HTTP header ' SOAPAction ' was not recognized by the server
提问by Daniel Newtown
When I send a SOAP request to the server it returns following error, though I send similar request using SoapUI and that works. It seems I need to change my SOAP request to the one that I am sending using SoapUI. WSDLis here.
当我向服务器发送 SOAP 请求时,它返回以下错误,尽管我使用 SoapUI 发送了类似的请求并且可以正常工作。看来我需要将我的 SOAP 请求更改为我使用 SoapUI 发送的请求。WSDL在这里。
[ truncated ] System.Web.Services.Protocols.SoapException : The value of the
HTTP header ' SOAPAction ' was not recognized by the server . \ r \ n at
System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest ( )
\ r \ n at System.Web.Servic
I am sending following request using Java
我正在使用 Java发送以下请求
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:SearchFlights xmlns:ns2="ElysArres.API">
<ns2:SoapMessage>
<ns2:Username>Test</ns2:Username>
<ns2:Password>TestPassword</ns2:Password>
<ns2:LanguageCode>EN</ns2:LanguageCode>
<ns2:Request>
<ns2:Departure>ONT</ns2:Departure>
<ns2:Destination>EWR</ns2:Destination>
<ns2:DepartureDate>2016-01-20</ns2:DepartureDate>
<ns2:ReturnDate>2016-01-28</ns2:ReturnDate>
<ns2:NumADT>1</ns2:NumADT>
<ns2:NumINF>0</ns2:NumINF>
<ns2:NumCHD>0</ns2:NumCHD>
<ns2:CurrencyCode>EUR</ns2:CurrencyCode>
<ns2:WaitForResult>true</ns2:WaitForResult>
<ns2:NearbyDepartures>true</ns2:NearbyDepartures>
<ns2:NearbyDestinations>true</ns2:NearbyDestinations>
<ns2:RROnly>false</ns2:RROnly>
<ns2:MetaSearch>false</ns2:MetaSearch>
</ns2:Request>
</ns2:SoapMessage>
</ns2:SearchFlights>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I can send following request using SoapUIand it works
我可以使用 SoapUI发送以下请求并且它可以工作
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:els="ElsyArres.API">
<soap:Header/>
<soap:Body>
<els:SearchFlights>
<els:SoapMessage>
<els:Username>Test</els:Username>
<els:Password>TestPassword</els:Password>
<els:LanguageCode>EN</els:LanguageCode>
<els:Request>
<els:Departure>ONT</els:Departure>
<els:Destination>EWR</els:Destination>
<els:DepartureDate>2016-01-20</els:DepartureDate>
<els:ReturnDate>2016-01-28</els:ReturnDate>
<els:NumADT>1</els:NumADT>
<els:NumINF>0</els:NumINF>
<els:NumCHD>0</els:NumCHD>
<els:CurrencyCode>EUR</els:CurrencyCode>
<els:WaitForResult>true</els:WaitForResult>
<els:NearbyDepartures>true</els:NearbyDepartures>
<els:NearbyDestinations>true</els:NearbyDestinations>
<els:RROnly>false</els:RROnly>
<els:MetaSearch>false</els:MetaSearch>
</els:Request>
</els:SoapMessage>
</els:SearchFlights>
</soap:Body>
</soap:Envelope>
I am not sure how to make the request that I am creating with Java same as what I am sending with SoapUI.
我不确定如何使我使用 Java 创建的请求与使用 SoapUI 发送的请求相同。
Code
代码
SearchFlights
搜索航班
@XmlRootElement(name = "SearchFlights")
@XmlAccessorType(XmlAccessType.FIELD)
public class SearchFlights {
@XmlElement(name = "SoapMessage")
private SoapMessage soapMessage;
getter and setter
SoapMessage
肥皂消息
@XmlRootElement(name = "SoapMessage")
@XmlAccessorType(XmlAccessType.FIELD)
public class SoapMessage {
@XmlElement(name = "Username")
private String username;
@XmlElement(name = "Password")
private String password;
@XmlElement(name = "LanguageCode")
private String languageCode;
@XmlElement(name = "Request")
private Request request;
getters and setters
Request
要求
@XmlRootElement(name = "Request")
@XmlAccessorType(XmlAccessType.FIELD)
public class Request {
@XmlElement(name = "Departure")
private String departure;
@XmlElement(name = "Destination")
private String destination;
@XmlElement(name = "DepartureDate")
private String departureDate;
@XmlElement(name = "ReturnDate")
private String returnDate;
@XmlElement(name = "NumADT")
private int numADT;
@XmlElement(name = "NumINF")
private int numInf;
@XmlElement(name = "NumCHD")
private int numCHD;
@XmlElement(name = "CurrencyCode")
private String currencyCode;
@XmlElement(name = "WaitForResult")
private boolean waitForResult;
@XmlElement(name = "NearByDepartures")
private boolean nearByDepartures;
@XmlElement(name = "NearByDestinations")
private boolean nearByDestinations;
@XmlElement(name = "RROnly")
private boolean rronly;
@XmlElement(name = "MetaSearch")
private boolean metaSearch;
getters and setters
package-info.java
包信息.java
@XmlSchema(
namespace = "ElsyArres.API",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.myproject.flights.wegolo;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
jaxb.index
jaxb.index
SearchFlights
Flight
Flights
Leg
Legs
Outbound
Request
Response
SoapMessage
Code to send request
发送请求的代码
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPConstants;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
......
// populate searchFlights and other classes to create request
try {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(
MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.myproject.flights.wegolo");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"http://www5v80.elsyarres.net/service.asmx",
searchFlights);
Response msg = (Response) response;
System.err.println("Wegolo >>>"
+ msg.getFlights().getFlight().size());
} catch (Exception s) {
s.printStackTrace();
}
Update
更新
I removed package-info.java
and managed to use the suggested code, but it is still sending the same header.
我删除package-info.java
并设法使用建议的代码,但它仍然发送相同的标头。
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"http://www5v80.elsyarres.net/service.asmx",
searchFlights,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message)
{
((SoapMessage)message).setSoapAction("http://www5v80.elsyarres.net/searchFlights");
}
}
);
采纳答案by AndyN
SOAP Version 1.1 requires a HTTP header in your SOAP request to specify the SOAP action. It's not in the actual XML, it's part of the request (in the HTTP header), so that is why you are not seeing any difference between your SoapUI request xml, and the request you're sending using the WebServiceTemplate. Soap 1.2 allows you to set it as an attribute on the media type, but that is not valid for a 1.1 server. Note that according to the specification, the value you use doesn't have to be resolvable.
SOAP 1.1 版要求您的 SOAP 请求中有一个 HTTP 标头来指定 SOAP 操作。它不在实际的 XML 中,它是请求的一部分(在 HTTP 标头中),所以这就是为什么您看不到 SoapUI 请求 xml 和您使用 WebServiceTemplate 发送的请求之间有任何区别的原因。Soap 1.2 允许您将其设置为媒体类型的属性,但这对 1.1 服务器无效。请注意,根据规范,您使用的值不必是可解析的。
SOAP places no restrictions on the format or specificity of the URI or that it is resolvable. An HTTP client MUST use this header field when issuing a SOAP HTTP Request.
SOAP 对 URI 的格式或特殊性或其可解析性没有任何限制。HTTP 客户端在发出 SOAP HTTP 请求时必须使用这个头域。
Usually, it's specified in your WSDL, something like (taken from here):
通常,它在您的 WSDL 中指定,类似于(取自此处):
<soap:operation
soapAction="http://www5v80.elsyarres.net/searchFlights"
style="document" />
If that is not in your WSDL, you can add it by using the action
annotation in spring in your webservice endpoint class.
如果这不在您的 WSDL 中,您可以通过action
在 web 服务端点类中使用spring 中的注释来添加它。
@Endpoint
public class MyFlightEndpoint{
@Action("http://www5v80.elsyarres.net/searchFlights")
public SearchFlights request() {
...
}
}
If it is in your WSDL, you'll want to place that value into your HTTP header on the client side. To do this, you then need to get access to the message on the client side after it's created, but before it's sent in order to add the action header. Spring provides a message callback interface for that, that's described here. What you'll want to do is something like:
如果它在您的 WSDL 中,您需要将该值放入客户端的 HTTP 标头中。为此,您需要在消息创建之后但在发送之前访问客户端上的消息,以便添加操作标头。Spring 为此提供了一个消息回调接口,在此处进行了描述。你想要做的是:
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"http://www5v80.elsyarres.net/service.asmx",
searchFlights,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message)
{
((SoapMessage)message).setSoapAction("http://www5v80.elsyarres.net/searchFlights");
}
}
);
There's a discussion on SOAP action headers here, and the point (or lack of a point) for them if you want to know more.
如果您想了解更多信息,这里有关于 SOAP 操作标头的讨论,以及它们的要点(或缺少要点)。
Edit: So looking at the wsdl here:
编辑:所以看看这里的 wsdl:
<soap:operation soapAction="ElsyArres.API/SearchFlights" style="document"/>
you'll want the following action:
您将需要以下操作:
ElsyArres.API/searchFlights
Now just update the code to read
现在只需更新代码即可阅读
((SoapMessage)message).setSoapAction("ElsyArres.API/searchFlights");
and you're good to go!
一切顺利!
Edit 2: I also notice the service you're connecting to accepts SOAP 1.2 connections, while you're using SOAP 1.1. You can force your client to use SOAP 1.2 by setting it in your factory.
编辑 2:我还注意到您正在连接的服务接受 SOAP 1.2 连接,而您正在使用 SOAP 1.1。您可以通过在工厂中进行设置来强制您的客户端使用 SOAP 1.2。
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();
It looks like the server uses the same endpoint, so that should be the only change.
看起来服务器使用相同的端点,所以这应该是唯一的变化。
回答by adnans
Another way to add SOAPAction
header when using WebServiceGatewaySupport
is to do the following:
SOAPAction
使用时添加标题的另一种方法WebServiceGatewaySupport
是执行以下操作:
getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback("http://httpheader/"));
This is using messageFactory.setSoapVersion(SoapVersion.SOAP_12);
这是使用 messageFactory.setSoapVersion(SoapVersion.SOAP_12);
回答by sashanet buryk
I had the same problem, my fix was :
我遇到了同样的问题,我的解决方法是:
@Configuration
public class SoapConfiguration {
private static final String SOAP_1_2_PROTOCOL= "SOAP 1.2 Protocol";
@Bean
public WebServiceTemplate webServiceTemplate() throws Exception {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("YOUR_WSDL_GENERATED_PATH");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
return webServiceTemplate;
}
And my SoapService
还有我的肥皂服务
@Service
@RequiredArgsConstructor
public class SoapDomainBoxService extends WebServiceGatewaySupport {
private final WebServiceTemplate webServiceTemplate;
public void searchFlights(SearchFlights searchFlights) {
String url = "YOUR.URL.asmx";
Response response = (Response) webServiceTemplate.marshalSendAndReceive(url, searchFlights, new SoapActionCallback("ACTION.CALLBACK"));
}
Very important on creation of message factory use
对创建消息工厂使用非常重要
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));