Spring-WS client not setting SOAPAction header
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)
Related videos on Youtube
![Steve L](https://i.stack.imgur.com/RzuV9.jpg?s=256&g=1)
Comments
-
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 over 9 yearsWorks like a charm, thanks saved me a lot of effort!
-
Michal Krasny almost 9 yearsI 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 almost 9 years@Michal you can always make it more parameterized by using properties files.
-
Zrom about 5 yearsNote that this solution doesn't work with Soap 1.2 since the SOAPAction header is deprecated.
-
Jaja over 3 yearsIndeed working correclty for Soap 1.1 as the soapAction was not automatically filled in.