JAVA中的SOAP请求:无法添加包含SOAP名称空间中的元素的片段

时间:2018-07-15 06:46:07

标签: java xml soap wsdl soap-client

我目前正在用Java编写一个独立的soap请求/响应应用程序,并且我正在使用以下项目:https://gist.github.com/kdelfour/b2a449a1bac23e3baec8

我正在使用此示例WSDL来开发代码:https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl

从上面的WSDL中,我试图调用以下操作: LatLonListCityNames 具有以下肥皂请求:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ndf="https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">
   <soapenv:Header/>
   <soapenv:Body>
      <ndf:LatLonListCityNames soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <displayLevel xsi:type="xsd:integer">1</displayLevel>
      </ndf:LatLonListCityNames>
   </soapenv:Body>
</soapenv:Envelope>

这是我的下面的代码:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * This is an example of a simple SOAP Client class to send request body to a
 * SOAP Server.
 *
 * Useful when you want to test a SOAP server and you don't want to generate all
 * SOAP client class from the WSDL.
 *
 * @author kdelfour
 */
public class ASimpleSOAPClient {

    // Default logger
    private static final Logger LOG = Logger.getLogger(ASimpleSOAPClient.class);

    // The SOAP server URI
    private String uriSOAPServer;
    // The SOAP connection
    private SOAPConnection soapConnection = null;

    // If you want to add namespace to the header, follow this constant
    private static final String PREFIX_NAMESPACE = "ndf";
    private static final String NAMESPACE = "https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl";

    /**
     * A constructor who create a SOAP connection
     *
     * @param url
     *            the SOAP server URI
     */
    public ASimpleSOAPClient(final String url) {
        this.uriSOAPServer = url;

        try {
            createSOAPConnection();
        } catch (UnsupportedOperationException | SOAPException e) {
            LOG.error(e);
        }
    }

    /**
     * Send a SOAP request for a specific operation
     *
     * @param xmlRequestBody
     *            the body of the SOAP message
     * @param operation
     *            the operation from the SOAP server invoked
     * @return a response from the server
     * @throws SOAPException
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public String sendMessageToSOAPServer(String xmlRequestBody, String operation) throws SOAPException, SAXException, IOException,
            ParserConfigurationException {

        // Send SOAP Message to SOAP Server
        final SOAPElement stringToSOAPElement = stringToSOAPElement(xmlRequestBody);
        final SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(stringToSOAPElement, operation), uriSOAPServer);

        // Print SOAP Response
        LOG.info("Response SOAP Message : " + soapResponse.toString());
        return soapResponse.toString();
    }

    /**
     * Create a SOAP connection
     *
     * @throws UnsupportedOperationException
     * @throws SOAPException
     */
    private void createSOAPConnection() throws UnsupportedOperationException,
            SOAPException {

        // Create SOAP Connection
        SOAPConnectionFactory soapConnectionFactory;
        soapConnectionFactory = SOAPConnectionFactory.newInstance();
        soapConnection = soapConnectionFactory.createConnection();
    }

    /**
     * Create a SOAP request
     *
     * @param body
     *            the body of the SOAP message
     * @param operation
     *            the operation from the SOAP server invoked
     * @return the SOAP message request completed
     * @throws SOAPException
     */
    private SOAPMessage createSOAPRequest(SOAPElement body, String operation)
            throws SOAPException {

        final MessageFactory messageFactory = MessageFactory.newInstance();
        final SOAPMessage soapMessage = messageFactory.createMessage();
        final SOAPPart soapPart = soapMessage.getSOAPPart();

        // SOAP Envelope
        final SOAPEnvelope envelope = soapPart.getEnvelope();
        envelope.addNamespaceDeclaration(PREFIX_NAMESPACE, NAMESPACE);

        // SOAP Body
        final SOAPBody soapBody = envelope.getBody();
        soapBody.addChildElement(body);

        // Mime Headers
        final MimeHeaders headers = soapMessage.getMimeHeaders();
        LOG.info("SOAPAction : " + uriSOAPServer + operation);
        headers.addHeader("SOAPAction", uriSOAPServer + operation);

        soapMessage.saveChanges();

        /* Print the request message */
        LOG.info("Request SOAP Message :" + soapMessage.toString());
        return soapMessage;
    }

    /**
     * Transform a String to a SOAP element
     *
     * @param xmlRequestBody
     *            the string body representation
     * @return a SOAP element
     * @throws SOAPException
     * @throws SAXException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    private SOAPElement stringToSOAPElement(String xmlRequestBody)
            throws SOAPException, SAXException, IOException,
            ParserConfigurationException {

        // Load the XML text into a DOM Document
        final DocumentBuilderFactory builderFactory = DocumentBuilderFactory
                .newInstance();
        builderFactory.setNamespaceAware(true);
        final InputStream stream = new ByteArrayInputStream(
                xmlRequestBody.getBytes());
        final Document doc = builderFactory.newDocumentBuilder().parse(stream);

        // Use SAAJ to convert Document to SOAPElement
        // Create SoapMessage
        final MessageFactory msgFactory = MessageFactory.newInstance();
        final SOAPMessage message = msgFactory.createMessage();
        final SOAPBody soapBody = message.getSOAPBody();

        // This returns the SOAPBodyElement that contains ONLY the Payload
        return soapBody.addDocument(doc);
    }

    public static void main(String[] args) throws SOAPException, SAXException, IOException, ParserConfigurationException {
        String url = "https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl";
        ASimpleSOAPClient soapClient = new ASimpleSOAPClient(url);
        String xmlMessage = "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ndf=\"https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl\">\r\n" + 
                "   <soapenv:Header/>\r\n" + 
                "   <soapenv:Body>\r\n" + 
                "      <ndf:LatLonListCityNames soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" + 
                "         <displayLevel xsi:type=\"xsd:integer\">1</displayLevel>\r\n" + 
                "      </ndf:LatLonListCityNames>\r\n" + 
                "   </soapenv:Body>\r\n" + 
                "</soapenv:Envelope>";
        String operation = "LatLonListCityNames";
        soapClient.sendMessageToSOAPServer(xmlMessage, operation);

    }
}

我已经尝试过 xmlMessage 的排列组合,但最终会出现不同的错误。 我尝试过的一种变体是:

String xmlMessage = "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\r\n" + 
                "   <soapenv:Header/>\r\n" + 
                "   <soapenv:Body>\r\n" + 
                "      <ndf:LatLonListCityNames soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" + 
                "         <displayLevel xsi:type=\"xsd:integer\">1</displayLevel>\r\n" + 
                "      </ndf:LatLonListCityNames>\r\n" + 
                "   </soapenv:Body>\r\n" + 
                "</soapenv:Envelope>";

在这里,我从信封中删除了ndf前缀元素。这给了我一个不同的错误,如下所示:

[Fatal Error] :4:98: The prefix "ndf" for element "ndf:LatLonListCityNames" is not bound.
Exception in thread "main" org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 98; The prefix "ndf" for element "ndf:LatLonListCityNames" is not bound.
    at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
    at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
    at ASimpleSOAPClient.stringToSOAPElement(ASimpleSOAPClient.java:159)
    at ASimpleSOAPClient.sendMessageToSOAPServer(ASimpleSOAPClient.java:78)
    at ASimpleSOAPClient.main(ASimpleSOAPClient.java:183)

我不确定我在做什么错。那么这里有指针吗?

提前谢谢

我也尝试了以下代码:

import java.io.*;
import java.net.*;

public class AnotherSoapClient {
    public static void main(String[] args) throws Exception {

        String SOAPUrl      = "https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl";
        String xmlFile2Send = "<soapenv:Envelope xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\" xmlns:xsd=\\\"http://www.w3.org/2001/XMLSchema\\\" xmlns:soapenv=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\" xmlns:ndf=\\\"https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl\\\">\\r\\n\" + \r\n" + 
                "               \"   <soapenv:Header/>\\r\\n\" + \r\n" + 
                "               \"   <soapenv:Body>\\r\\n\" + \r\n" + 
                "               \"      <ndf:LatLonListCityNames soapenv:encodingStyle=\\\"http://schemas.xmlsoap.org/soap/encoding/\\\">\\r\\n\" + \r\n" + 
                "               \"         <displayLevel xsi:type=\\\"xsd:integer\\\">1</displayLevel>\\r\\n\" + \r\n" + 
                "               \"      </ndf:LatLonListCityNames>\\r\\n\" + \r\n" + 
                "               \"   </soapenv:Body>\\r\\n\" + \r\n" + 
                "               \"</soapenv:Envelope>\";\r\n" + 
                "        String operation = \"LatLonListCityNames";

          String SOAPAction = "LatLonListCityNames";

        // Create the connection where we're going to send the file.
        URL url = new URL(SOAPUrl);
        URLConnection connection = url.openConnection();
        HttpURLConnection httpConn = (HttpURLConnection) connection;

        // Open the input file. After we copy it to a byte array, we can see
        // how big it is so that we can set the HTTP Cotent-Length
        // property. (See complete e-mail below for more on this.)

        byte[] b = xmlFile2Send.getBytes();

        // Set the appropriate HTTP parameters.
        httpConn.setRequestProperty( "Content-Length",
                                     String.valueOf( b.length ) );
        httpConn.setRequestProperty("Content-Type","text/xml; charset=utf-8");
        httpConn.setRequestProperty("SOAPAction",SOAPAction);
        httpConn.setRequestMethod( "POST" );
        httpConn.setDoOutput(true);
        httpConn.setDoInput(true);

        // Everything's set up; send the XML that was read in to b.
        OutputStream out = httpConn.getOutputStream();
        out.write( b );    
        out.close();

        // Read the response and write it to standard out.

        InputStreamReader isr =
            new InputStreamReader(httpConn.getInputStream());
        BufferedReader in = new BufferedReader(isr);

        String inputLine;

        while ((inputLine = in.readLine()) != null)
            System.out.println(inputLine);

        in.close();
    }

  // copy method from From E.R. Harold's book "Java I/O"
  public static void copy(InputStream in, OutputStream out) 
   throws IOException {

    // do not allow other threads to read from the
    // input or write to the output while copying is
    // taking place

    synchronized (in) {
      synchronized (out) {

        byte[] buffer = new byte[256];
        while (true) {
          int bytesRead = in.read(buffer);
          if (bytesRead == -1) break;
          out.write(buffer, 0, bytesRead);
        }
      }
    }
  } 
}

我收到以下错误:

Exception in thread "main" java.net.ConnectException: Connection timed out: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.connect(Unknown Source)
    at sun.security.ssl.BaseSSLSocketImpl.connect(Unknown Source)
    at sun.net.NetworkClient.doConnect(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.<init>(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.New(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
    at AnotherSoapClient.main(AnotherSoapClient.java:41)

我能够通过浏览器或soap-ui访问该URL。我不确定我在这里发生什么

再次感谢您。

2 个答案:

答案 0 :(得分:0)

您的代码存在多个潜在的问题。将来,您应该在帖子中坚持一个特定的问题和/或错误,以便我们能够清楚地回答。

用WSDL来实现SOAP API可能很棘手,因为涉及的元素太多了。关于您的第一个错误示例:“前缀ndf ...未绑定”- 此错误表示您违反为此SOAP API定义的XML模式。你不能那样做。您需要提供/绑定ndf前缀或调整架构。这将解决您的XML分析器错误。这个问题很常见,您可以在这里找到更多信息:

xml schema validation error "prefix is not bound"

导致ndf错误的代码的主要问题是:您从以下位置删除了ndf模式声明:

<soapenv:Envelope ...

在您的xmlMessage中。如果将其从此处删除,则不能在标记名称中将其包含在其中,例如:

<ndf:LatLonListCityNames soapenv:encodingStyle ...

您可以将所有内容标记为适当的名称空间(ndf,xsd等)或将其删除。您还可以指示代码在解析器中忽略名称空间问题/验证,但随后可能在其他地方导致应用程序出现问题。

在提供的链接中有关于此的更多信息。您还将受益于Web搜索“ Java xml命名空间”,并对此做一些进一步的阅读。

关于第二个错误示例: 当您的代码尝试连接到Weather.gov上的SSL / TLS服务器时,您会收到超时错误。您是否始终收到该错误?如果是这样,则需要增加记录器的详细程度,以便了解发生了什么情况。

答案 1 :(得分:0)

我无法修复第一个代码,但是我可以通过遵循此答案How do I make HttpURLConnection use a proxy?添加代理配置来使第二个代码起作用 由于我可以解决阻止程序的问题,因此我将此问题标记为已回答