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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:51:45  来源:igfitidea点击:

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

javaspringsoapjaxbspring-web

提问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.in​​dex

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

enter image description here

在此处输入图片说明

采纳答案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 actionannotation 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 SOAPActionheader when using WebServiceGatewaySupportis 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));