web-dev-qa-db-fra.com

Le client Spring-WS ne définit pas l'en-tête SOAPAction

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'.
14
Steve L

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;
}
3
Steve L

Une réponse complète va comme suit.

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.

35
Atais

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 SoapMessagepuisse ê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 WebTemplatename__:

WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
ClientInterceptor[] interceptors = new ClientInterceptor[]{new SecurityInterceptor(parameters)};
webServiceTemplate.setInterceptors();
webServiceTemplate.marshalSendAndReceive(uriWebService, request)
0
Mike D3ViD Tyson