简体   繁体   English

如何将数据从SOAP处理程序传递回Web服务客户端?

[英]How can I pass data back from a SOAP handler to a webservice client?

(Following up on this question: Getting raw XML response from Java web service client ) (关注这个问题: 从Java Web服务客户端获取原始XML响应

I've got a SOAP message handler that is able to get the raw XML of a web service response. 我有一个SOAP消息处理程序,它能够获取Web服务响应的原始XML。 I need to get this XML into the webservice client so I can perform some XSL transformations on the response before sending it on its way. 我需要将这个XML放到webservice客户端中,这样我就可以在响应之前对响应执行一些XSL转换。 I'm having trouble figuring out a good way to get data from a SOAP handler that catches incoming messages, and makes the raw XML available to a generated (from a WSDL) web service client. 我无法找到一种从捕获传入消息的SOAP处理程序中获取数据的好方法,并使原始XML可用于生成(来自WSDL)Web服务客户端。 Any ideas if this is even feasible? 如果这是可行的任何想法?


I've come up with something like this: 我想出了这样的事情:

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());
        }
    }

In very simple tests, this seems to work. 在非常简单的测试中,这似乎有效。 But this solution feels somewhat like a cheap hack. 但这个解决方案感觉有点像廉价黑客。 I don't like setting the raw XML as a member of the chain handler, and I have a gut feeling this violates many other best practices. 我不喜欢将原始XML设置为链处理程序的成员,我有一种直觉,这违反了许多其他最佳实践。 Does anyone have a more elegant way of doing this? 有没有人有更优雅的方式这样做?

The two choices that seemed to work for me are both documented here . 这两个似乎对我有用的选择都记录在这里 I didn't receive a response yet about whether using a ThreadLocal was fine or not, but I don't see why it shouldn't be. 我还没有收到关于使用ThreadLocal是否合适的回复,但我不明白它为什么不应该。

My secoond method which was added to the original question was to go the route of the handler. 我的secoond方法被添加到原始问题是去处理程序的路线。 While debugging the WS callout, I noticed that the invocationProperties map had the SOAP response as part of an internal packet structure within the responseContext object, but there appeared to be no way of getting to it. 在调试WS标注时,我注意到invocationProperties映射将SOAP响应作为responseContext对象内部数据包结构的一部分,但似乎没有办法实现它。 The ResponseContext was a set of name value pairs. ResponseContext是一组名称值对。 However, when I read the source code for ResponseContext at this location , I saw that the code for the get method had a comment about returning null if it could not find an Application Scoped property, otherwise, it would read it from the packet invocationProperties, which seemed to be what I wanted. 但是,当我在这个位置读取ResponseContext的源代码时,我看到get方法的代码有一个关于返回null的注释,如果它找不到Application Scoped属性,否则,它将从数据包invocationProperties中读取它,这似乎是我想要的。 So I seached on how to set the scope on the key/value pair (Google: setting application-scope property for jaxws) that the context was introducing it low-and-behold, it was in the jax-ws spec that I referenced in the other thread. 所以我谈到了如何在键/值对上设置范围(Google:为jaxws设置应用程序范围属性)上下文引入它的内容很低,这是我在jax-ws规范中引用的另一个线程。

I also did some reading about the Packet, https://jax-ws.java.net/nonav/jax-ws-20-fcs/arch/com/sun/xml/ws/api/message/Packet.html . 我还做了一些关于Packet的阅读, https://jax-ws.java.net/nonav/jax-ws-20-fcs/arch/com/sun/xml/ws/api/message/Packet.html

I hope this makes some sense for you. 我希望这对你有意义。 I was concerned that three wouldn't be anything to use JAXB against if the web service call resulted in a Soap FAULT, and I really wanted to log this packet, since it was being returned from a Payment Gateway which to this day has a number of undocumented results. 我担心如果Web服务调用导致Soap FAULT,三个不会使用JAXB,我真的想记录这个数据包,因为它是从支付网关返回的,直到今天还有一个数字没有记录的结果。

Good luck. 祝好运。

The solution was to use JAXB to convert the objects back to XML. 解决方案是使用JAXB将对象转换回XML。 I didn't really want to do this because it seems redundant to have the webservice client receive XML, convert it to a POJO, only to have that POJO converted back to XML, but it works. 我真的不想这样做,因为让webservice客户端接收XML,将其转换为POJO似乎是多余的,只是将该POJO转换回XML,但它可以工作。

Example of handler that passes out request / response message bodies: 传递请求/响应消息体的处理程序示例:

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) { }
}

To register handler and use preserved properties: 要注册处理程序并使用保留的属性:

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 TL; DR

Metro JAX WS RI docs says about MessageContext.Scope.APPLICATION property: Metro JAX WS RI文档说明了MessageContext.Scope.APPLICATION属性:

The message context object can also hold properties set by the client or provider. 消息上下文对象还可以包含客户端或提供者设置的属性。 For instance, port proxy and dispatch objects both extend BindingProvider . 例如,端口代理和调度对象都扩展了BindingProvider A message context object can be obtained from both to represent the request or response context. 可以从两者获得消息上下文对象以表示请求或响应上下文。 Properties set in the request context can be read by the handlers, and the handlers may set properties on the message context objects passed to them. 处理程序可以读取请求上下文中设置的属性,处理程序可以设置传递给它们的消息上下文对象的属性。 If these properties are set with the scope MessageContext.Scope.APPLICATION then they will be available in the response context to the client. 如果使用范围MessageContext.Scope.APPLICATION设置这些属性,则它们将在客户端的响应上下文中可用。 On the server end, a context object is passed into the invoke method of a Provider . 在服务器端,将上下文对象传递到Provider的invoke方法。

metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/api/message/Packet.java contains property: 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;

On other hand metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/client/ResponseContext.java is an implementation of Map with: 另一方面, 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;
}

In SOAPHandler we can mark property as APPLICATION instead of default MessageContext.Scope.HANDLER : 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};

by: 通过:

/**
 * 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM