【问题标题】:Customising JAX-WS prefix of a SOAP response定制 SOAP 响应的 JAX-WS 前缀
【发布时间】:2012-10-05 02:11:57
【问题描述】:

目标

我正在为一个相当旧的(但遗憾的是不可更改的)界面实现 Web 服务。我遇到了一个问题,即调用我的服务的客户端在 SOAP 响应中需要某个命名空间,而我很难将其更改为匹配。

考虑一个 hello world 示例,我想要这个:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:helloResponse xmlns:ns2="http://test/">
         <return>Hello Catchwa!</return>
      </ns2:helloResponse>
   </S:Body>
</S:Envelope>

看起来像这样:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <customns:helloResponse xmlns:customns="http://test/">
         <return>Hello Catchwa!</return>
      </customns:helloResponse>
   </S:Body>
</S:Envelope>

我发现了类似于我正在尝试做的事情here,但我无法让类似的代码正确执行。 (我想坚持使用 Metro 而不必更改为 cxf 或轴)


执行

返回JAXBRIContext 的JAXBContextFactory 实现如下所示:

import com.sun.xml.bind.api.JAXBRIContext;
import com.sun.xml.bind.api.TypeReference;
import com.sun.xml.ws.api.model.SEIModel;
import com.sun.xml.ws.developer.JAXBContextFactory;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;

public class HelloJaxbContext implements JAXBContextFactory
{
  @Override
  public JAXBRIContext createJAXBContext(SEIModel seim, List<Class> classesToBind, List<TypeReference> typeReferences) throws JAXBException {
    List<Class> classList = new ArrayList<Class>();
    classList.addAll(classesToBind);

    List<TypeReference> refList = new ArrayList<TypeReference>();
    for (TypeReference tr : typeReferences) {
        refList.add(new TypeReference(new QName(tr.tagName.getNamespaceURI(), tr.tagName.getLocalPart(), "customns"), tr.type, tr.annotations));
    }
    return JAXBRIContext.newInstance(classList.toArray(new Class[classList.size()]), refList, null, seim.getTargetNamespace(), false, null);
  }  
}

Web 服务的一些测试代码很简单:

import com.sun.xml.ws.developer.UsesJAXBContext;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;

@WebService(serviceName = "Hello")
@UsesJAXBContext(value = HelloJaxbContext.class)
public class Hello
{
  @WebMethod(operationName = "hello")
  public String hello(@WebParam(name = "name") String txt)
  {
    return "Hello " + txt + "!";
  }
}

问题

在使用 jaxws-rt 2.2.7(来自 Maven)的 Tomcat 7.0.32 和 Glassfish 3.1.2 中,上述代码不会影响我的 Web 服务输出(命名空间前缀仍然是“ns2”)。

【问题讨论】:

  • 您是在 Tomcat 中运行此 Web 服务,还是在 Java 发行版中使用 Metro?它们有不同的实现,因此也有不同的解决方案。
  • 我正在使用或多或少的默认 NetBeans 配置将其部署到 Tomcat。
  • 所以我的回答对你没有帮助。您需要通过 Tomcat 源来获取正确的信息。

标签: java soap jaxb jax-ws


【解决方案1】:

如果您从旧服务的 WSDL 开始,并使用 wsimport 生成所有各种 JAXB 注释的请求和响应包装器类,那么在生成的包中您应该找到一个 package-info.java 例如

@javax.xml.bind.annotation.XmlSchema(namespace = "http://test/")
package com.example.test;

JAXB 提供了一种机制让您在 @XmlSchema 注释上建议前缀映射,因此您可以尝试修改 package-info.java 以读取

@javax.xml.bind.annotation.XmlSchema(namespace = "http://test/",
   xmlns = { 
      @javax.xml.bind.annotation.XmlNs(prefix = "customns", 
         namespaceURI="http://test/")
   }
)
package com.example.test;

看看这对生成的消息是否有任何影响。这还具有纯 JAXB 规范的优势(即不依赖于 RI 特定的自定义上下文工厂)。

如果您需要重新运行wsimport,您可以通过将-npa 选项传递给xjc 来防止它覆盖您修改的package-info(这告诉它不要生成package-info.java,而是将所有必要的namespace 设置在类级注释上)。具体如何执行此操作取决于您运行wsimport 的方式。

命令行:

wsimport -B-npa ....

蚂蚁:

<wsimport wsdl="..." destdir="..." .... >
  <xjcarg value="-npa" />
</wsimport>

马文:

<plugin>
  <groupId>org.jvnet.jax-ws-commons</groupId>
  <artifactId>jaxws-maven-plugin</artifactId>
  <version>2.2</version>
  <executions>
    <execution>
      <goals>
        <goal>wsimport</goal>
      </goals>
      <configuration>
        <xjcArgs>
          <xjcArg>-npa</xjcArg>
        </xjcArgs>
      </configuration>
    </execution>
  </executions>
</plugin>

【讨论】:

  • 谢谢,这行得通!你知道我对package-info.java 的修改是否可以在我进行清理时被覆盖,还是应该单独存储它,然后在 wsimport mvn 任务运行后将其复制?
  • 用XJC的,有一个Namespace Prefix插件。
【解决方案2】:

实现您想要实现的目标的推荐/标准方法是使用SOAPMessage Handler。它们类似于 java web 应用程序过滤器(理论上也可以在这里工作),因为它们用于实现Chain of Responsibility pattern。例如,在你的情况下,你可以有这个:

import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;


public class SOAPBodyHandler implements SOAPHandler<SOAPMessageContext> {

static final String DESIRED_NS_PREFIX = "customns";
static final String DESIRED_NS_URI = "http://test/";
static final String UNWANTED_NS_PREFIX = "ns";

@Override
public Set<QName> getHeaders() {
   //do nothing
   return null;
}

@Override
public boolean handleMessage(SOAPMessageContext context) {
    if ((boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)) { //Check here that the message being intercepted is an outbound message from your service, otherwise ignore.
        try {
            SOAPEnvelope msg = context.getMessage().getSOAPPart().getEnvelope(); //get the SOAP Message envelope
            SOAPBody body = msg.getBody();
            body.removeNamespaceDeclaration(UNWANTED_NS_PREFIX);
            body.addNamespaceDeclaration(DESIRED_NS_PREFIX, DESIRED_NS_URI); 
        } catch (SOAPException ex) {
            Logger.getLogger(SOAPBodyHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    return true; //indicates to the context to proceed with (normal)message processing
}

@Override
public boolean handleFault(SOAPMessageContext context) {
      //do nothing
   return null;
}

@Override
public void close(MessageContext context) {
      //do nothing

}

}

在您的服务实现 Bean 类声明中,添加

  @HandlerChain(file = "handler-chain.xml")

上面的注释是对配置文件的引用,它实际上允许你的处理程序启动。配置文件看起来像这样

  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
     <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <javaee:handler-chain>
           <javaee:handler>
              <javaee:handler-class>your.handler.FQN.here</javaee:handler-class>
           </javaee:handler>
        </javaee:handler-chain>
     </javaee:handler-chains> 

在家里试试这个。此特定代码尚未经过测试

【讨论】:

  • 我已经尝试过您的代码,但它更改了 SOAP 响应的 Body 元素的名称空间和前缀,而不是 JAX-WS 生成的内容。即您的代码将命名空间声明添加到 Body 元素,但不会更改 helloResponse 元素的命名空间声明。感谢您的尝试!
  • 我已经成功使用消息处理程序自定义命名空间前缀,详细大纲在这里:stackoverflow.com/a/43316912/149895
【解决方案3】:

在参考实现中,我这样做是为了最终使它工作。见schemaLocation ignored when marshalling JAXB Classes using Metro

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2016-08-23
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多