【问题标题】:Getting raw XML SOAP-response on client side using ADB-stubs created by AXIS2使用 AXIS2 创建的 ADB 存根在客户端获取原始 XML SOAP 响应
【发布时间】:2012-08-23 21:21:58
【问题描述】:

我使用 AXIS2 创建的 ADB 存根访问 SOAP 服务。我想记录服务返回的任何 Axis Fault 的原始 XML 响应。我可以将这些错误捕获为“ServiceError”。但是,我没有找到检索原始 XML 的方法(参见下面的示例)。

我找到了一种使用 getOMElement 访问原始 XML 请求/响应以进行常规处理的方法(参见下面的示例)。但是,这不适用于故障。

如何使用 ADB 存根获取原始 XML 错误?

示例 Java 代码:

    public void testRequest(String URL) throws AxisFault {
        MyServiceStub myservice = new MyServiceStub(URL);
        MyRequest req = new MyRequest();
        try {
            TypeMyFunctionResponse response = myservice.myFunction(req);

            // logging full soap response
            System.out.println("SOAP Response: "
                    + response.getOMElement(null,
                            OMAbstractFactory.getOMFactory())
                            .toStringWithConsume());
        } catch (RemoteException e) {
            //...
        } catch (ServiceError e) {
            // how to get the raw xml?
        }
    }

我想获取并记录的故障响应示例:

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <soapenv:Fault>
            <soapenv:Code>
                <soapenv:Value>soapenv:Receiver</soapenv:Value>
            </soapenv:Code>
            <soapenv:Reason>
                <soapenv:Text xml:lang="en-US">service error</soapenv:Text>
            </soapenv:Reason>
            <soapenv:Detail>
                <ns1:error xmlns:ns1="http://www.somehost.com/webservices/someservice">
                    <ns1:code>500</ns1:code>
                    <ns1:messageText>some fault message</ns1:messageText>
                </ns1:error>
            </soapenv:Detail>
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>

【问题讨论】:

  • 这可能不是您想要的,但是编写一个 JAX-WS 处理程序来记录故障呢?使用处理程序,您可以访问 SOAP 消息。例如:mkyong.com/webservices/jax-ws/…
  • 谢谢!这可能是解决方案。但是,我需要进行相当大的代码更改并用 JAX-WS 替换 AXIS2。因此,如果有任何方法可以通过保持当前框架以更少的努力解决这个问题,我会非常高兴。
  • 一些澄清:您不需要用 JAX-WS “替换” Axis2。 JAX-WS 是 Java Web 服务(@WebService 注释等)的规范,Axis2 是它的实现之一。 jdk 包含 JAX-WS 的参考实现,您也可以单独使用它,但 Axis2 是一个替代方案。不过,您应该能够在当前设置旁边使用处理程序(因为 Axis2 实现了 JAX-WS)。
  • 我现在按照你的建议做了。以前我的cmets不够精确。我不得不用 JAX-WS (wsimport) 生成的客户端替换我的 AXIS2-ADB-stubs。但是,这并没有我预期的那么耗时。我现在也将删除 AXIS2。在我看来,无论我使用 jdk-reference 实现还是 AXIS2,都没有太大区别。
  • 只要您不需要任何花哨的标准,例如 WS-Security,它并没有真正的不同。很高兴听到我的建议很有用。如果这是解决方案,那么演示记录 SOAP 错误的处理程序的答案将是合适的。你提供,还是我应该提供?

标签: java soap axis2


【解决方案1】:

对于 Axis2,那些没有改变实现的奢侈/或出于 xyz 原因不想使用 JAS-WS 的人,

发现@Ducane 很有用

request = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("Out")
         .getEnvelope().toString());

response = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
         .getEnvelope().toString());

正如@dayer 的回答中所建议的那样

response = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
     .getEnvelope().toString());

失败,出现 com.ctc.wstx.exc.WstxIOException 异常和消息:>尝试在关闭的流上读取。

不确定“In”消息标签有什么问题,

但在搜索时,发现以下 JIRA 票 https://issues.apache.org/jira/browse/AXIS2-5469 指向 https://issues.apache.org/jira/browse/AXIS2-5202 在讨论中发现使用以下代码解决此问题的 WA 之一,我能够收听soapRequest 的响应消息。

stub._getServiceClient().getAxisService().addMessageContextListener(
new MessageContextListener() {
    public void attachServiceContextEvent(ServiceContext sc,
        MessageContext mc) {}
    public void attachEnvelopeEvent(MessageContext mc) {
        try
        { mc.getEnvelope().cloneOMElement().serialize(System.out); }
        catch (XMLStreamException e) {}
    }
});

因为这里 MessageContextListner 是参数定义的匿名内部类 它将可以访问所有封闭变量, 所以我只是将一个字符串类变量定义为 latestSoapResponse 并存储响应以供进一步使用。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
mc.getEnvelope().cloneOMElement().serialize(baos); 
latestSoapResponse=baos.toString();

请注意,您需要在生成soap请求之前添加监听器。 并且 Request MessageContext 仅在您生成肥皂请求后才可用。

还有那些只想要原始肥皂请求响应以进行调试的人 可能会看到来自@Sanker、here 的回答,以使用 JVM 参数启用 Apache 公共日志记录。

【讨论】:

    【解决方案2】:

    关于杜坎的回复:

    response = yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
              .getEnvelope().toString());
    

    失败并出现 com.ctc.wstx.exc.WstxIOException 异常和消息:Attempted read on closed stream

    【讨论】:

      【解决方案3】:

      以下是您可能正在寻找的内容,yourStub 是您通过 wsdl2java 生成的内容,并在您提出请求后使用以下行。该消息设置为lastOperation,并在您进行实际呼叫时发送:

      request = yourStub._getServiceClient().getLastOperationContext().getMessageContext("Out")
                    .getEnvelope().toString());
      
      response = yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
                    .getEnvelope().toString());
      

      希望对您有所帮助。

      【讨论】:

        【解决方案4】:

        按照 joergl 的建议,我使用“SOAPHandler”将 ADB-stub 更改为 JAX-WS-ones,以按照此处的描述记录请求、响应和故障:http://www.mkyong.com/webservices/jax-ws/jax-ws-soap-handler-in-client-side/

        我的处理程序看起来像这样,用于使用 log4j 记录格式良好的 XML:

        public class RequestResponseHandler  implements SOAPHandler<SOAPMessageContext> {
        
            private static Logger log = Logger.getLogger(RequestResponseHandler.class);
            private Transformer transformer = null;
            private DocumentBuilderFactory docBuilderFactory = null;
            private DocumentBuilder docBuilder = null;
        
            public RequestResponseHandler() {
                try {
                    transformer = TransformerFactory.newInstance().newTransformer();
                    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "5");
                    docBuilderFactory = DocumentBuilderFactory.newInstance();
                    docBuilder = docBuilderFactory.newDocumentBuilder();
                } catch (TransformerConfigurationException
                        | TransformerFactoryConfigurationError
                        | ParserConfigurationException e) {
                    log.error(e.getMessage(), e);
                }
            }
        
            @Override
            public void close(MessageContext arg0) {
            }
        
            @Override
            public boolean handleFault(SOAPMessageContext messageContext) {
                log(messageContext);
                return true;
            }
        
            @Override
            public boolean handleMessage(SOAPMessageContext messageContext) {
                log(messageContext);
                return true;
            }
        
            private void log(SOAPMessageContext messageContext) {
                String xml = "";
                SOAPMessage msg = messageContext.getMessage();
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                try {
                    msg.writeTo(out);
                    xml = out.toString("UTF-8");
                } catch (Exception e) {
                    log.error(e.getMessage(),e);
                }       
        
                String direction = "";
                Boolean outbound = (Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); 
                if (outbound) { 
                    direction += "Request: \n"; 
                } else { 
                    direction += "Response: \n";
                } 
        
                log.info(direction + getXMLprettyPrinted(xml));     
            }
        
            @Override
            public Set<QName> getHeaders() {
                return Collections.emptySet();
            }
        
        
            public String getXMLprettyPrinted(String xml) {
        
                if (transformer == null || docBuilder == null)
                    return xml;
        
                InputSource ipXML = new InputSource(new StringReader(xml));
                Document doc;
        
                try {
                    doc = docBuilder.parse(ipXML);
                    StringWriter stringWriter = new StringWriter();
                    StreamResult streamResult = new StreamResult(stringWriter);
                    DOMSource domSource = new DOMSource(doc);
                    transformer.transform(domSource, streamResult);
                    return stringWriter.toString();
                } catch (SAXException | IOException | TransformerException e) {
                    log.error(e.getMessage(), e);
                    return xml;
                }
            }
        }
        

        此外,我想在我的应用程序代码中重用原始 XML。所以我不得不将这些数据从 SOAPHandler 传输回我的客户端代码。如何做到这一点并不太明显。更多关于这个问题的信息可以在这篇文章中找到: How to send additional fields to soap handler along with soapMessage?

        【讨论】:

          【解决方案5】:

          虽然这个问题已经得到了很好的回答,但我需要早点做,并且找不到适合我的限制的合适答案,所以我为后代添加自己的答案。

          我需要在最近运行 JDK 1.4 的项目中使用 Axis 2 版本 1.4.1 来执行此操作,据我所知,JAX-WS 存根不支持该项目。我最终保留了 ADB 存根,同时通过用我自己的构建器类包装 SoapBuilder、复制输入流并将副本传递给 SoapBuilder 来捕获输入:

          public class SOAPBuilderWrapper implements Builder {
              private String lastResponse;
          
              private SOAPBuilder builder = new SOAPBuilder();
          
              private static final int BUFFER_SIZE = 8192;
          
              public OMElement processDocument(InputStream inputStream,
                      String contentType, MessageContext messageContext) throws AxisFault {
                  ByteArrayOutputStream copiedStream = new ByteArrayOutputStream();
                  try {
                      byte[] buffer = new byte[BUFFER_SIZE];
                      int bytesRead = inputStream.read(buffer);
                      while (bytesRead > -1) {
                          copiedStream.write(buffer, 0, bytesRead);
                          bytesRead = inputStream.read(buffer);
                      }
                      lastResponse = copiedStream.toString();
          
                  } catch (IOException e) {
                      throw new AxisFault("Can't read from input stream", e);
                  }
                  return builder.processDocument(
                          new ByteArrayInputStream(copiedStream.toByteArray()),
                          contentType, messageContext);
              }
          
              public String getLastResponse() {
                  return lastResponse;
              }
          }
          

          由于各种原因,使用axis2.xml进行配置存在问题,因此以编程方式添加了包装器:

          SoapBuilderWrapper responseCaptor = new SoapBuilderWrapper();
          AxisConfiguration axisConfig = stub._getServiceClient().getAxisConfiguration();
          axisConfig.addMessageBuilder("application/soap+xml", responseCaptor);
          axisConfig.addMessageBuilder("text/xml", responseCaptor);
          

          这允许在调用服务后使用 responseCaptor.getLastResponse() 检索响应。

          【讨论】:

          • 嗨 Taufiq,但是我如何打印请求?我在axis2上,但没有切换到JAX-WS的选项
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-21
          • 1970-01-01
          • 2012-04-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多