【问题标题】:How To Modify The Raw XML message of an Outbound CXF Request?如何修改出站 CXF 请求的原始 XML 消息?
【发布时间】:2011-08-02 16:36:09
【问题描述】:

我想修改一个传出的 SOAP 请求。 我想从信封的正文中删除 2 个 xml 节点。 我设法设置了一个拦截器并将生成的消息集的字符串值设置到端点。

但是,以下代码似乎无法正常工作,因为未按预期编辑传出消息。有没有人有一些关于如何做到这一点的代码或想法?

public class MyOutInterceptor extends AbstractSoapInterceptor {

public MyOutInterceptor() {
        super(Phase.SEND); 
}

public void handleMessage(SoapMessage message) throws Fault { 
        // Get message content for dirty editing...
        StringWriter writer = new StringWriter();
        CachedOutputStream cos  = (CachedOutputStream)message.getContent(OutputStream.class); 
        InputStream inputStream = cos.getInputStream();
        IOUtils.copy(inputStream, writer, "UTF-8");
        String content = writer.toString();

        // remove the substrings from envelope...
        content = content.replace("<idJustification>0</idJustification>", "");
        content = content.replace("<indicRdv>false</indicRdv>", "");
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        outputStream.write(content.getBytes(Charset.forName("UTF-8")));
        message.setContent(OutputStream.class, outputStream);
} 

【问题讨论】:

    标签: java web-services cxf interceptor


    【解决方案1】:

    基于第一条评论,我创建了一个抽象类,可以很容易地用来改变整个肥皂信封。

    以防万一有人想要一个现成的代码部分。

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
    import org.apache.cxf.io.CachedOutputStream;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.apache.log4j.Logger;
    
    /**
     * http://www.mastertheboss.com/jboss-web-services/apache-cxf-interceptors
     * http://stackoverflow.com/questions/6915428/how-to-modify-the-raw-xml-message-of-an-outbound-cxf-request
     * 
     */
    public abstract class MessageChangeInterceptor extends AbstractPhaseInterceptor<Message> {
    
        public MessageChangeInterceptor() {
            super(Phase.PRE_STREAM);
            addBefore(SoapPreProtocolOutInterceptor.class.getName());
        }
    
        protected abstract Logger getLogger();
    
        protected abstract String changeOutboundMessage(String currentEnvelope);
    
        protected abstract String changeInboundMessage(String currentEnvelope);
    
        public void handleMessage(Message message) {
            boolean isOutbound = false;
            isOutbound = message == message.getExchange().getOutMessage()
                    || message == message.getExchange().getOutFaultMessage();
    
            if (isOutbound) {
                OutputStream os = message.getContent(OutputStream.class);
    
                CachedStream cs = new CachedStream();
                message.setContent(OutputStream.class, cs);
    
                message.getInterceptorChain().doIntercept(message);
    
                try {
                    cs.flush();
                    IOUtils.closeQuietly(cs);
                    CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
    
                    String currentEnvelopeMessage = IOUtils.toString(csnew.getInputStream(), "UTF-8");
                    csnew.flush();
                    IOUtils.closeQuietly(csnew);
    
                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("Outbound message: " + currentEnvelopeMessage);
                    }
    
                    String res = changeOutboundMessage(currentEnvelopeMessage);
                    if (res != null) {
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("Outbound message has been changed: " + res);
                        }
                    }
                    res = res != null ? res : currentEnvelopeMessage;
    
                    InputStream replaceInStream = IOUtils.toInputStream(res, "UTF-8");
    
                    IOUtils.copy(replaceInStream, os);
                    replaceInStream.close();
                    IOUtils.closeQuietly(replaceInStream);
    
                    os.flush();
                    message.setContent(OutputStream.class, os);
                    IOUtils.closeQuietly(os);
    
                } catch (IOException ioe) {
                    getLogger().warn("Unable to perform change.", ioe);
                    throw new RuntimeException(ioe);
                }
            } else {
                try {
                    InputStream is = message.getContent(InputStream.class);
                    String currentEnvelopeMessage = IOUtils.toString(is, "UTF-8");
                    IOUtils.closeQuietly(is);
    
                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("Inbound message: " + currentEnvelopeMessage);
                    }
    
                    String res = changeInboundMessage(currentEnvelopeMessage);
                    if (res != null) {
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("Inbound message has been changed: " + res);
                        }
                    }
                    res = res != null ? res : currentEnvelopeMessage;
    
                    is = IOUtils.toInputStream(res, "UTF-8");
                    message.setContent(InputStream.class, is);
                    IOUtils.closeQuietly(is);
                } catch (IOException ioe) {
                    getLogger().warn("Unable to perform change.", ioe);
    
                    throw new RuntimeException(ioe);
                }
            }
        }
    
        public void handleFault(Message message) {
        }
    
        private class CachedStream extends CachedOutputStream {
            public CachedStream() {
                super();
            }
    
            protected void doFlush() throws IOException {
                currentStream.flush();
            }
    
            protected void doClose() throws IOException {
            }
    
            protected void onWrite() throws IOException {
            }
        }
    }
    

    【讨论】:

    • CachedStream 来自哪里?我没有看到它的导入,也找不到它。
    • Cached Stream 是 apache SVN 中示例的内部类。 svn.apache.org/viewvc/cxf/trunk/distribution/src/main/release/… 提供的抽象类就像一个魅力! :)
    • 为什么这段代码在处理更大的消息时会失败?行message.getInterceptorChain().doIntercept(message) 留言内容null
    • 如果有人对如何将其附加到生成的 Apache Cxf 客户端感兴趣,可以简单地使用 Client client = ClientProxy.getClient(someServicePort); client.getInInterceptors().add(new MessageChangeInterceptor() { .. }); 以便可以修改 RAW 响应
    • 对不起......这是一个非常糟糕的代码。它可能有效,但正如@shx 所经历的那样,这完全不是运气。输入和输出流被无缘无故地创建,当它们为空时刷新,无缘无故地交换到消息中,当作者没有打开它们时关闭(意味着流可能有双重关闭),等等。另外,如果消息写入是通过编写器而不是输出流完成的(例如带有 messageType = TEXT 的 JMS 上的 SOAP),它根本不起作用。基本原理是好的(使用拦截器,访问流......)但实现真的是错误的。
    【解决方案2】:

    以下能够冒泡服务器端异常。在之前的解决方案中使用 os.close() 而不是 IOUtils.closeQuietly(os) 也能够引发异常。

    public class OutInterceptor extends AbstractPhaseInterceptor<Message> {     
        public OutInterceptor() { 
            super(Phase.PRE_STREAM); 
            addBefore(StaxOutInterceptor.class.getName()); 
        }   
        public void handleMessage(Message message) { 
            OutputStream os = message.getContent(OutputStream.class); 
            CachedOutputStream cos = new CachedOutputStream(); 
            message.setContent(OutputStream.class, cos); 
            message.getInterceptorChain.aad(new PDWSOutMessageChangingInterceptor(os)); 
        }
    } 
    
    public class OutMessageChangingInterceptor extends AbstractPhaseInterceptor<Message> {
        private OutputStream os; 
    
        public OutMessageChangingInterceptor(OutputStream os){
            super(Phase.PRE_STREAM_ENDING); 
            addAfter(StaxOutEndingInterceptor.class.getName()); 
            this.os = os;
        } 
    
        public void handleMessage(Message message) { 
            try { 
                CachedOutputStream csnew = (CachedOutputStream) message .getContent(OutputStream.class);
                String currentEnvelopeMessage = IOUtils.toString( csnew.getInputStream(), (String) message.get(Message.ENCODING)); 
                csnew.flush(); 
                IOUtils.closeQuietly(csnew); 
                String res = changeOutboundMessage(currentEnvelopeMessage); 
                res = res != null ? res : currentEnvelopeMessage; 
                InputStream replaceInStream = IOUtils.tolnputStream(res, (String) message.get(Message.ENCODING)); 
                IOUtils.copy(replaceInStream, os); 
                replaceInStream.close(); 
                IOUtils.closeQuietly(replaceInStream);
                message.setContent(OutputStream.class, os);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);  
            }
        }
    } 
    

    【讨论】:

    • 欢迎来到 Stack Overflow!尽管此答案可能是正确且有用的,但如果您随附一些解释以解释它如何帮助解决问题,则最好。如果有更改(可能不相关)导致它停止工作并且用户需要了解它曾经是如何工作的,这在未来变得特别有用。
    【解决方案3】:

    基于this替换出站soap内容的好例子

    package kz.bee.bip;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import org.apache.commons.io.IOUtils;
    import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
    import org.apache.cxf.io.CachedOutputStream;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    
    public class SOAPOutboundInterceptor extends AbstractPhaseInterceptor<Message> {
    
        public SOAPOutboundInterceptor() {
            super(Phase.PRE_STREAM);
            addBefore(SoapPreProtocolOutInterceptor.class.getName());
        }
    
        public void handleMessage(Message message) {
    
            boolean isOutbound = false;
            isOutbound = message == message.getExchange().getOutMessage()
                    || message == message.getExchange().getOutFaultMessage();
    
            if (isOutbound) {
                OutputStream os = message.getContent(OutputStream.class);
                CachedStream cs = new CachedStream();
                message.setContent(OutputStream.class, cs);
    
                message.getInterceptorChain().doIntercept(message);
    
                try {
                    cs.flush();
                    IOUtils.closeQuietly(cs);
                    CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
    
                    String currentEnvelopeMessage = IOUtils.toString(csnew.getInputStream(), "UTF-8");
                    csnew.flush();
                    IOUtils.closeQuietly(csnew);
    
                    /* here we can set new data instead of currentEnvelopeMessage*/
                    InputStream replaceInStream = IOUtils.toInputStream(currentEnvelopeMessage, "UTF-8");
    
                    IOUtils.copy(replaceInStream, os);
                    replaceInStream.close();
                    IOUtils.closeQuietly(replaceInStream);
    
                    os.flush();
                    message.setContent(OutputStream.class, os);
                    IOUtils.closeQuietly(os);
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
    
        public void handleFault(Message message) {
        }
    
        private static class CachedStream extends CachedOutputStream {
            public CachedStream() {
                super();
            }
    
            protected void doFlush() throws IOException {
                currentStream.flush();
            }
    
            protected void doClose() throws IOException {
            }
    
            protected void onWrite() throws IOException {
            }
        }
    }
    
    

    【讨论】:

      【解决方案4】:

      这个对我有用。它基于 Apache CXF 示例中 configuration_interceptor 示例中的 StreamInterceptor 类。

      它在 Scala 而不是 Java 中,但转换很简单。

      我尝试添加 cmets 来解释发生了什么(据我所知)。

      import java.io.OutputStream
      
      import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor
      import org.apache.cxf.helpers.IOUtils
      import org.apache.cxf.io.CachedOutputStream
      import org.apache.cxf.message.Message
      import org.apache.cxf.phase.AbstractPhaseInterceptor
      import org.apache.cxf.phase.Phase
      
      // java note: base constructor call is hidden at the end of class declaration
      class StreamInterceptor() extends AbstractPhaseInterceptor[Message](Phase.PRE_STREAM) {
      
        // java note: put this into the constructor after calling super(Phase.PRE_STREAM);
        addBefore(classOf[SoapPreProtocolOutInterceptor].getName)
      
        override def handleMessage(message: Message) = {
          // get original output stream
          val osOrig = message.getContent(classOf[OutputStream])
          // our output stream
          val osNew = new CachedOutputStream
          // replace it with ours
          message.setContent(classOf[OutputStream], osNew)
          // fills the osNew instead of osOrig
          message.getInterceptorChain.doIntercept(message)
          // flush before getting content
          osNew.flush()
          // get filled content
          val content = IOUtils.toString(osNew.getInputStream, "UTF-8")
          // we got the content, we may close our output stream now
          osNew.close()
          // modified content
          val modifiedContent = content.replace("a-string", "another-string")
          // fill original output stream
          osOrig.write(modifiedContent.getBytes("UTF-8"))
          // flush before set
          osOrig.flush()
          // replace with original output stream filled with our modified content
          message.setContent(classOf[OutputStream], osOrig)
        }
      }
      
      

      【讨论】:

        【解决方案5】:

        更好的方法是使用 DOM 接口修改消息,您需要先添加 SAAJOutInterceptor(这可能会影响大请求的性能),然后是在 USER_PROTOCOL 阶段执行的自定义拦截器

        import org.apache.cxf.binding.soap.SoapMessage;
        import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
        import org.apache.cxf.interceptor.Fault;
        import org.apache.cxf.phase.Phase;
        import org.w3c.dom.Node;
        
        import javax.xml.soap.SOAPException;
        import javax.xml.soap.SOAPMessage;
        
        abstract public class SoapNodeModifierInterceptor extends AbstractSoapInterceptor {
            SoapNodeModifierInterceptor() { super(Phase.USER_PROTOCOL); }
        
            @Override public void handleMessage(SoapMessage message) throws Fault {
                try {
                    if (message == null) {
                        return;
                    }
                    SOAPMessage sm = message.getContent(SOAPMessage.class);
                    if (sm == null) {
                        throw new RuntimeException("You must add the SAAJOutInterceptor to the chain");
                    }
        
                    modifyNodes(sm.getSOAPBody());
        
                } catch (SOAPException e) {
                    throw new RuntimeException(e);
                }
            }
        
            abstract void modifyNodes(Node node);
        }
        

        【讨论】:

          【解决方案6】:

          我今天也遇到了这个问题。在哭泣和咬牙切齿之后,我能够在 CXF 源附带的 configuration_interceptor 演示中更改 StreamInterceptor 类:

          OutputStream os = message.getContent(OutputStream.class);
          CachedStream cs = new CachedStream();
          message.setContent(OutputStream.class, cs);
          
          message.getInterceptorChain().doIntercept(message);
          
          try {
              cs.flush();
              CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
          
              String soapMessage = IOUtils.toString(csnew.getInputStream());
              ...
          

          soapMessage 变量将包含完整的 SOAP 消息。您应该能够操作soap 消息,将其刷新到输出流并执行message.setContent(OutputStream.class... 调用以对消息进行修改。这不提供任何保证,因为我自己对 CXF 还很陌生!

          注意:CachedStream 是 StreamInterceptor 类中的一个私有类。不要忘记将您的拦截器配置为在 PRE_STREAM 阶段运行,以便 SOAP 拦截器有机会编写 SOAP 消息。

          【讨论】:

          • 感谢您的意见,约翰。可以在此处找到与该问题相关的其他元素:stackoverflow.com/questions/6906499/…
          • 在您的示例中,OutputStream 操作系统已声明但未使用:您在此代码中如何需要它?
          猜你喜欢
          • 2015-07-16
          • 2013-04-19
          • 1970-01-01
          • 2017-07-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多