【问题标题】:How can I pass data back from a SOAP handler to a webservice client?如何将数据从 SOAP 处理程序传回 Web 服务客户端?
【发布时间】:2012-08-14 08:32:15
【问题描述】:

(跟进这个问题:Getting raw XML response from Java web service client

我有一个 SOAP 消息处理程序,它能够获取 Web 服务响应的原始 XML。我需要将此 XML 放入 Web 服务客户端,以便在发送响应之前对响应执行一些 XSL 转换。我无法找到一种从 SOAP 处理程序获取数据的好方法,该处理程序捕获传入的消息,并使原始 XML 可用于生成的(来自 WSDL)Web 服务客户端。如果这甚至可行,有什么想法吗?


我想出了这样的东西:

public class CustomSOAPHandler implements javax.xml.ws.handler.soap.SOAPHandler<javax.xml.ws.handler.soap.SOAPMessageContext>
{
    private String myXML;
    public String getMyXML()
    {
        return myXML;
    }
    ...
    public boolean handleMessage(SOAPMessageContext context)
    {
        ...
        myXML = this.getRawXML(context.getMessage());
    }

    //elsewhere in the application:
    ...
    myService.doSomething(someRequest);
    for (Handler h: ((BindingProvider)myService).getBinding().getHandlerChain())
    {
        if (h instanceof CustomSOAPHandler )
        {
            System.out.println("HandlerResult: "+ ((CustomSOAPHandler )h).getMyXML());
        }
    }

在非常简单的测试中,这似乎可行。但这个解决方案有点像廉价的黑客攻击。我不喜欢将原始 XML 设置为链处理程序的成员,而且我直觉这违反了许多其他最佳实践。有没有人有更优雅的方式来做到这一点?

【问题讨论】:

  • 可以在HttpSession中设置一个DOM对象吗?
  • @davidfmatheson:也许吧,但我为什么要使用 DOM 对象而不是字符串,这才是我真正想要的?

标签: java xml web-services soaphandler


【解决方案1】:

作为替代方案,您可以将其放入ThreadLocal,而不是将请求/响应详细信息放入soap 上下文中(在我的情况下它不起作用)。所以你需要@gavenkoa 描述的SOAPHandler (ty),但是将它添加到ThreadLocal 实例中,而不是soap 上下文中。

【讨论】:

    【解决方案2】:

    传递请求/响应消息体的处理程序示例:

    public class MsgLogger implements SOAPHandler<SOAPMessageContext> {
    
        public static String REQEST_BODY = "com.evil.request";
        public static String RESPONSE_BODY = "com.evil.response";
    
        @Override
        public Set<QName> getHeaders() {
            return null;
        }
    
        @Override
        public boolean handleMessage(SOAPMessageContext context) {
            SOAPMessage msg = context.getMessage();
            Boolean beforeRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream(32_000);
                context.getMessage().writeTo(baos);
                String key = beforeRequest ? REQEST_BODY : RESPONSE_BODY;
                context.put(key, baos.toString("UTF-8"));
                context.setScope(key, MessageContext.Scope.APPLICATION);
            } catch (SOAPException | IOException e) { }
            return true;
        }
    
        @Override
        public boolean handleFault(SOAPMessageContext context) {
            return true;
        }
    
        @Override
        public void close(MessageContext context) { }
    }
    

    注册处理程序并使用保留的属性:

    BindingProvider provider = (BindingProvider) port;
    List<Handler> handlerChain = bindingProvider.getBinding().getHandlerChain();
    handlerChain.add(new MsgLogger());
    bindingProvider.getBinding().setHandlerChain(handlerChain);
    
    Req req = ...;
    Rsp rsp = port.serviceCall(req); // call WS Port
    
    // Access saved message bodies:
    Map<String, Object> responseContext = provider.getResponseContext();
    String reqBody = (String) responseContext.get(MsgLogger.REQEST_BODY);
    String rspBody = (String) responseContext.get(MsgLogger.RESPONSE_BODY);
    

    TL;DR

    Metro JAX WS RI docsMessageContext.Scope.APPLICATION 属性:

    消息上下文对象还可以保存客户端或提供者设置的属性。例如,端口代理和调度对象都扩展BindingProvider。可以从两者中获取消息上下文对象来表示请求或响应上下文。处理程序可以读取请求上下文中设置的属性,并且处理程序可以在传递给它们的消息上下文对象上设置属性。如果这些属性设置为MessageContext.Scope.APPLICATION 范围,那么它们将在客户端的响应上下文中可用。在服务器端,一个上下文对象被传递给Provider的invoke方法。

    metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/api/message/Packet.java 包含属性:

    /**
     * Lazily created set of handler-scope property names.
     *
     * <p>
     * We expect that this is only used when handlers are present
     * and they explicitly set some handler-scope values.
     *
     * @see #getHandlerScopePropertyNames(boolean)
     */
    private Set<String> handlerScopePropertyNames;
    

    另一方面,metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/client/ResponseContext.javaMap 的一个实现:

    public boolean containsKey(Object key) {
        if(packet.supports(key))
            return packet.containsKey(key);    // strongly typed
    
        if(packet.invocationProperties.containsKey(key))
            // if handler-scope, hide it
            return !packet.getHandlerScopePropertyNames(true).contains(key);
    
        return false;
    }
    

    SOAPHandler 中,我们可以将属性标记为APPLICATION 而不是默认的MessageContext.Scope.HANDLER

    /**
     * Property scope. Properties scoped as <code>APPLICATION</code> are
     * visible to handlers,
     * client applications and service endpoints; properties scoped as
     * <code>HANDLER</code>
     * are only normally visible to handlers.
     */
    public enum Scope {APPLICATION, HANDLER};
    

    作者:

    /**
     * Sets the scope of a property.
     *
     * @param name Name of the property associated with the
     *             <code>MessageContext</code>
     * @param scope Desired scope of the property
     * @throws java.lang.IllegalArgumentException if an illegal
     *             property name is specified
     */
    public void setScope(String name,  Scope scope);
    

    【讨论】:

      【解决方案3】:

      似乎对我有用的两个选择都记录在 here 中。我还没有收到关于使用 ThreadLocal 是否合适的回复,但我不明白为什么不应该这样做。

      我添加到原始问题的第二个方法是走处理程序的路线。在调试 WS 标注时,我注意到 invocationProperties 映射将 SOAP 响应作为 responseContext 对象中内部数据包结构的一部分,但似乎无法访问它。 ResponseContext 是一组名称值对。但是,当我在this location 阅读 ResponseContext 的源代码时,我看到 get 方法的代码有一条关于如果找不到 Application Scoped 属性则返回 null 的注释,否则,它将从数据包 invocationProperties 中读取它,这似乎是我想要的。因此,我搜索了如何在键/值对上设置范围(Google:为 jaxws 设置应用程序范围属性),上下文正在低调地引入它,它在我引用的 jax-ws 规范中另一个线程。

      我还阅读了一些关于数据包的信息,https://jax-ws.java.net/nonav/jax-ws-20-fcs/arch/com/sun/xml/ws/api/message/Packet.html

      我希望这对您有所帮助。我担心如果 Web 服务调用导致 Soap FAULT,那么三个将无法使用 JAXB,并且我真的很想记录这个数据包,因为它是从支付网关返回的,而该网关迄今为止有一个数字未记录的结果。

      祝你好运。

      【讨论】:

        【解决方案4】:

        解决方案是使用 JAXB 将对象转换回 XML。我真的不想这样做,因为让 web 服务客户端接收 XML,将其转换为 POJO,只是将 POJO 转换回 XML,这似乎是多余的,但它可以工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-09
          • 1970-01-01
          • 2011-10-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-09
          • 2010-12-23
          相关资源
          最近更新 更多