【问题标题】:How to extract SOAP header from a WS response using spring-ws and jaxb如何使用 spring-ws 和 jaxb 从 WS 响应中提取 SOAP 标头
【发布时间】:2014-12-08 16:14:19
【问题描述】:

我们在我们的应用程序上使用 spring-ws 2.2 来使用 Web 服务。很长一段时间以来,我们一直很高兴地这样做,并且一切正常,除了现在我需要访问响应中的 SOAP 标头,而我只是找不到这样做的方法。

我们正在使用配置有 Jaxb2Marshaller 的 WebServiceTemplate(来自 springs-ws)。 jaxb 文件是使用 xjc 从 wsdl 生成的。我的回复中的标题元素如下所示:

   <soapenv:Header>
         <v1:ResponseHeader status="OK">
             <v1:description>test</v1:description>
          </v1:ResponseHeader>
   </soapenv:Header>

在我的 java 类中,解析响应的代码如下所示(我已经剥离了一些不相关的代码):

public CalculationData getValues(Integer id) throws IntegrationException {
    WebServiceMessageCallback callback = createCallback(soapAction);

    GetValuesRequest request = toGetValues(id);
    GetValuesResponse response = null;
    try {
        response = (GetValuesResponse) webServiceTemplate.marshalSendAndReceive(request,         callback);
    } catch (SOAPFaultException fault) {
        log.error("Soap fault occurred during getValues " + id);
        throw new IntegrationException(fault);
    }

    CalculationData data = fromGetValues(response);

    return data;
}

请帮助我找到从响应中提取 SOAP 标头信息的解决方案。我必须能够解析作为属性发送的状态代码。

顺便说一句。我还有一个从模式生成的 ResponseHeader.java jaxb 类。

最终更改更新:

这是内联 ClientInterceptor 实现后我的 handleResponse 方法的样子:

@Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
    SoapMessage message = (SoapMessage) messageContext.getResponse();
    Iterator<SoapHeaderElement> responseHeaderElements =
            message.getSoapHeader().examineAllHeaderElements();
    SoapHeaderElement header = null;
    if (responseHeaderElements.hasNext()) {
        header = responseHeaderElements.next();
    } else {
        log.error("Error! No ResponseHeader found in response.");
        return false;
    }

    String responseCode = header.getAttributeValue(new QName(STATUS_QNAME));
    responseMsg.put(RESPONSE_MSG_KEY, responseCode);
    return true;
}

我尝试通过 QName 获取 ResponseHeader 元素,但由于某种原因这似乎不起作用。但是,无论如何,我只希望在肥皂标题中获得一个元素,这样可以正常工作吗?

【问题讨论】:

    标签: java spring jaxb spring-ws


    【解决方案1】:

    对于这个用例,最好的解决方案是使用自定义WebServiceMessageExtractor,如下所述:

    http://veithen.github.io/2015/01/03/spring-ws-extracting-soap-headers-from-responses.html

    【讨论】:

    • 是的,这似乎比通过匿名拦截器将模板绑定到特定的调用代码段更好。好的。最好在此处添加您的解决方案以供其他人查看。
    • 这看起来很有希望。我希望我有时间更好地了解这个解决方案并改用它。它似乎允许为 WebServiceExtractor 创建一个单独的类以供多个服务使用。就我而言,我有三个服务,它们具有在 WSDL 中定义的完全相同的响应标头。我希望能够使用相同的行为来提取标题数据而无需复制代码。
    【解决方案2】:

    实现一个ClientInterceptor,具体见handleResponse()方法。

    为了访问 Soap 标头,请转换为 SoapMessage

    public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
            QName v1ResponseHeaderQName = null;//todo
            QName statusAttrQName = null;//todo
            SoapMessage message = (SoapMessage) messageContext.getResponse();
            Iterator<SoapHeaderElement> matchingHeaders = message.getSoapHeader().examineHeaderElements(v1ResponseHeaderQName);
            String status = matchingHeaders.next().getAttributeValue(statusAttrQName);
    }
    

    然后拨打webServiceTemplate.setInterceptors(..)

    有关这些内容的更多示例,请参阅AbstractWsSecurityInterceptor 及其子类。但是请注意,这些拦截器处理替换请求消息,您只想阅读响应消息。

    问题是您现在正在处理原始肥皂消息,因此您失去了良好的弹簧编组,需要开始处理命名空间 (QNames) 和 w3c Dom 内容。

    为了让拦截器将标头传递回调用代码,您可以将拦截器设置为在 getValues(...) 方法中设置的匿名内部类。

    final Map<String,String> headers = new HashMap<>();
    
    template.setInterceptors(new ClientInterceptor[]{new ClientInterceptor() {
    
            @Override
            public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
                return true;
            }
    
            @Override
            public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
                headers.put("foo", "bar");
                return false;
            }
    
            @Override
            public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
                return true;
            }
    
            @Override
            public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
            }
    }});
    template.marshalSendAndReceive(....);
    

    【讨论】:

    • 谢谢丹的回答。我正在研究这种使用拦截器的方法。问题是我不明白如何访问在 getValues 中的 handleResponse 期间提取的信息。仅从拦截器返回 true 或 false 可能就足够了,但理想的情况是我可以将处理调用结果的代码放入 getValues。在这种方法中,当我在响应中收到不同类型的错误(或警告)并决定返回什么时,我有分叉。我还需要将它用于比我展示的更复杂的操作。
    • 在调用模板之前使用匿名拦截器设置。我将添加一个示例
    • 嗨,丹。这似乎是一个很好的解决方案。我将在完成当前任务后立即对此进行测试(我将此更改推迟到以后的 sprint)。我会在完成后立即将结果与您联系并在此处发布。非常感谢您的帮助!
    • 我现在已经完成了我的 Web 服务客户端的错误处理。奇迹般有效!感谢丹的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多