服务器无法识别 HTTP header“SOAPAction”的值

The value of the HTTP header ' SOAPAction ' was not recognized by the server

当我向服务器发送 SOAP 请求时,它 returns 出现以下错误,尽管我使用 SoapUI 发送了类似的请求并且可以正常工作。看来我需要将我的 SOAP 请求更改为我使用 SoapUI 发送的请求。 WSDLhere.

 [ 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

我正在使用 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>

我可以使用 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>

我不确定如何使我使用 Java 创建的请求与我使用 SoapUI 发送的请求相同。

代码

搜索航班

@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

请求

@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

@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

SearchFlights
Flight
Flights
Leg
Legs
Outbound
Request
Response
SoapMessage

发送请求的代码

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();
    }

更新

我删除了 package-info.java 并设法使用了建议的代码,但它仍然发送相同的 header。

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");
                                }
                           }
                       );

SOAP 1.1 版要求在您的 SOAP 请求中使用 HTTP header 来指定 SOAP 操作。它不在实际 XML 中,它是请求的一部分(在 HTTP header 中),所以这就是为什么您看不到 SoapUI 请求 xml 和请求之间有任何区别的原因您正在使用 WebServiceTemplate 发送。 Soap 1.2 允许您将其设置为媒体类型的属性,但这对 1.1 服务器无效。请注意,根据 specification,您使用的值不必是可解析的。

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.

通常,它在您的 WSDL 中指定,类似于(取自 here):

<soap:operation
        soapAction="http://www5v80.elsyarres.net/searchFlights"
        style="document" />

如果它不在您的 WSDL 中,您可以使用 spring 中的 action 注释在您的网络服务端点 class.

中添加它
@Endpoint
public class MyFlightEndpoint{
    @Action("http://www5v80.elsyarres.net/searchFlights")
    public SearchFlights request() {
        ...
    }
}

如果它在您的 WSDL 中,您需要将该值放入客户端的 HTTP header 中。为此,您需要在消息创建后但在发送之前访问客户端的消息,以便添加操作 header。 Spring 为此提供了一个消息回调接口,如 here 所述。你想要做的是:

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");
                        }
                   }
               );

有关于 SOAP 操作 headers here 的讨论,如果您想了解更多,还有对他们的要点(或缺少要点)。

编辑:所以在这里查看 wsdl:

<soap:operation soapAction="ElsyArres.API/SearchFlights" style="document"/>

您需要执行以下操作:

ElsyArres.API/searchFlights

现在只需更新代码即可阅读

((SoapMessage)message).setSoapAction("ElsyArres.API/searchFlights");

一切顺利!

编辑 2:我还注意到您连接的服务接受 SOAP 1.2 连接,而您使用的是 SOAP 1.1。您可以通过在您的工厂中进行设置来强制您的客户端使用 SOAP 1.2。

messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();

看起来服务器使用相同的端点,所以这应该是唯一的变化。

在使用 WebServiceGatewaySupport 时添加 SOAPAction header 的另一种方法是执行以下操作:

getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback("http://httpheader/"));

这正在使用 messageFactory.setSoapVersion(SoapVersion.SOAP_12);

我遇到了同样的问题,我的解决方法是:

  @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;
}

还有我的 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"));
}

创建消息工厂非常重要

   SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));