J'envoie une demande SOAP et le serveur se plaint que l'en-tête SOAPAction est vide. Je pense que je règle le problème, mais évidemment je ne le suis pas. Wireshark montre que ce n'est pas réglé.
@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();
}
}
L'erreur que je reçois du serveur dit:
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'.
J'ai travaillé sur cela, mais jamais posté la réponse. Voici ce que j'ai fini avec qui fonctionne bien:
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;
}
Bien que vous utilisiez WebServiceTemplate
en tant que classe pour communiquer avec le service Web, je ne comprends pas pourquoi, mais cela ne remplit pas correctement l'en-tête HTTP.
Certains WSDL ont une partie disant:
<soap:operation
soapAction="SOMELINK"
style="document" />
Et la WebServiceTemplate
ignore cette partie. L'erreur ci-dessus signifie que votre paramètre soapAction
dans l'en-tête est vide. Et ce ne devrait pas être le cas. Vérifiez auprès de Wireshark. Je l'ai fait - en utilisant certains clients Chrome Soap et Spring. Le second a un en-tête invalide.
Pour résoudre ce problème, vous devez suivre la section 6.2.4 ici: http://docs.spring.io/spring-ws/sites/2.0/reference/html/client.html
Cela dit, en gros, vous ajoutez vous-même la partie en-tête, avec l'interface WebServiceMessageCallback
. Vous pouvez lire plus dans la référence.
En gros, ça finit comme ça:
webServiceTemplate.marshalSendAndReceive(o, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
((SoapMessage)message).setSoapAction("http://tempuri.org/Action");
}
});
Où vous pouvez configurer correctement la valeur de l'en-tête. Travaillé pour moi aussi. Journée entière de lecture.
Une autre solution consiste à ajouter un interceptor et à définir la commande soapAction dans la méthode handleRequest()
afin que le système entrant reçoive le MessageContext
à partir duquel le SoapMessage
puisse être dérivé;
après cela, vous pouvez facilement définir soapAction en appelant la méthode setSoapAction()
.
voici le code de la classe d'intercepteur:
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..
}
et bien sûr, ajoutez l'intercepteur au WebTemplate
name__:
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
ClientInterceptor[] interceptors = new ClientInterceptor[]{new SecurityInterceptor(parameters)};
webServiceTemplate.setInterceptors();
webServiceTemplate.marshalSendAndReceive(uriWebService, request)