Spring-WS client not setting SOAPAction header

34,769

Solution 1

I worked this out but never posted the answer. Here's what I ended up with that works well:

public WebServiceTemplate getWebServiceTemplate() throws SOAPException {
  if (webServiceTemplate == null) {
    final MessageFactory msgFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
    final SaajSoapMessageFactory newSoapMessageFactory = new SaajSoapMessageFactory(msgFactory);
    webServiceTemplate = new WebServiceTemplate(newSoapMessageFactory);
  }   

  return webServiceTemplate;
}

public Object sendReceive(Object requestObject, ArrayList<String> classesToMarshall, final String action)
        throws ClassNotFoundException, SoapFaultException, SoapFaultClientException, WebServiceTransportException,
        IllegalStateException, SOAPException {

  final WebServiceTemplate wst = getWebServiceTemplate();

    final SoapMarshallUtil smu = getSoapMarshallUtil();
    smu.configureMarshaller(wst, classesToMarshall);

    // soap 1.2
    SoapActionCallback requestCallback = new SoapActionCallback(action) {
        public void doWithMessage(WebServiceMessage message) {
            SaajSoapMessage soapMessage = (SaajSoapMessage) message;
            SoapHeader soapHeader = soapMessage.getSoapHeader();

            QName wsaToQName = new QName("http://www.w3.org/2005/08/addressing", "To", "wsa");
            SoapHeaderElement wsaTo =  soapHeader.addHeaderElement(wsaToQName);
            wsaTo.setText(uri);

            QName wsaActionQName = new QName("http://www.w3.org/2005/08/addressing", "Action", "wsa");
            SoapHeaderElement wsaAction =  soapHeader.addHeaderElement(wsaActionQName);
            wsaAction.setText(action);
        }
    };

    Object responseObject = wst.marshalSendAndReceive(this.uri, requestObject, requestCallback);
    return responseObject;
}

Solution 2

A complete answer goes as follow.

While you are using WebServiceTemplate as a class to communicate with the Webservice, I do not understand why but it does not properly fill the HTTP Header.

Some WSDL have a part saying:

<soap:operation
            soapAction="SOMELINK"
            style="document" />

And the WebServiceTemplate ignores this part. The above error means that your soapAction parameter in the header is empty. And it should be not. Check with Wireshark. I did - using some Chrome Soap client and Spring. The second one has an invalid header.


To fix this you need to follow Section 6.2.4 in here: http://docs.spring.io/spring-ws/sites/2.0/reference/html/client.html

What it says is basically add the header part on your own, with WebServiceMessageCallback interface. You can read more in the reference.

Basically it ends up like this:

 webServiceTemplate.marshalSendAndReceive(o, new WebServiceMessageCallback() {

    public void doWithMessage(WebServiceMessage message) {
        ((SoapMessage)message).setSoapAction("http://tempuri.org/Action");
    }
});

Where you can set up properly the header value. Worked for me too. Whole day of reading.

Solution 3

Another walk-around is to add an interceptor and set the soapAction within the handleRequest() method that inbound receives the MessageContext from which the SoapMessage can be derived;

after that you can easily set the soapAction invoking the setSoapAction() method.

here is the code of the interceptor class:

public class SecurityInterceptor implements ClientInterceptor {

    @Override
    public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {

        SoapMessage soapMessage = (SoapMessage) messageContext.getRequest();
        soapMessage.setSoapAction("mySoapAction");    

        return true;
    }

    //TODO:: other methods and constructor..
}

and of course add the interceptor to the WebTemplate:

WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
ClientInterceptor[] interceptors = new ClientInterceptor[]{new SecurityInterceptor(parameters)};
webServiceTemplate.setInterceptors();
webServiceTemplate.marshalSendAndReceive(uriWebService, request)
Share:
34,769

Related videos on Youtube

Steve L
Author by

Steve L

Ex Software Developer, Real Estate Investor, Cyclist

Updated on July 05, 2022

Comments

  • Steve L
    Steve L almost 2 years

    I'm sending a SOAP request and the server is complaining that the SOAPAction header is empty. I think I'm setting it right, but obviously I'm not. Wireshark shows it's not set.

    @Test
    public void testLogin() throws Exception {
        StringBuffer loginXml = new StringBuffer();
        loginXml.append("<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:ns=\"http://example.com/xyz/2010/08\">");
        loginXml.append("  <soapenv:Header>");
        loginXml.append("    <ns:loginOperationDetails>");
        loginXml.append("    </ns:loginOperationDetails>");
        loginXml.append("  </soapenv:Header>");
        loginXml.append("  <soapenv:Body>");
        loginXml.append("    <ns:LogIn>");
        loginXml.append("      <ns:logInInfo>");
        loginXml.append("        <ns:CustomerAccountId>customer1</ns:CustomerAccountId>");
        loginXml.append("        <ns:Username>JDoe</ns:Username>");
        loginXml.append("        <ns:Password>abc123</ns:Password>");
        loginXml.append("      </ns:logInInfo>");
        loginXml.append("    </ns:LogIn>");
        loginXml.append("  </soapenv:Body>");
        loginXml.append("</soapenv:Envelope>");
    
        WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
        MessageFactory msgFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
        SaajSoapMessageFactory newSoapMessageFactory = new SaajSoapMessageFactory(msgFactory);
        webServiceTemplate.setMessageFactory(newSoapMessageFactory);
    
        String uri = "http://xyz.example.com/xyz_1.0/membership.svc/ws";
        webServiceTemplate.setDefaultUri(uri);
    
        StreamSource source = new StreamSource(new StringReader(loginXml.toString()));
        StreamResult result = new StreamResult(System.out);
    
        boolean resultReturned = false;
        try {
            resultReturned = webServiceTemplate.sendSourceAndReceiveToResult(source, 
                new SoapActionCallback("http://example.com/xyz/2010/08/MembershipService/LogIn"), 
                result);
        } 
        catch (SoapFaultClientException sfe) {
            logger.error("SoapFaultClientException resultReturned: " + resultReturned, sfe);
            fail();
        }
    }
    

    The error I'm getting back from the server says:

    500 Internal Server Error
    The SOAP action specified on the message, '', does not match the HTTP SOAP Action, 'http://example.com/xyz/2010/08/MembershipService/LogIn'.
    
  • Marius
    Marius over 9 years
    Works like a charm, thanks saved me a lot of effort!
  • Michal Krasny
    Michal Krasny almost 9 years
    I wonder if it can be done automatically. Adding this hard-coded value works for me, but it is not very nice to clutter my code with things like this.
  • Atais
    Atais almost 9 years
    @Michal you can always make it more parameterized by using properties files.
  • Zrom
    Zrom about 5 years
    Note that this solution doesn't work with Soap 1.2 since the SOAPAction header is deprecated.
  • Jaja
    Jaja over 3 years
    Indeed working correclty for Soap 1.1 as the soapAction was not automatically filled in.

Related