自定义 SOAP 响应的 JAX-WS 前缀

问题描述:

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

I'm implementing a web service for quite an old (but sadly unchangeable) interface. I have an issue where the client that is calling my service expects a certain namespace in the SOAP response and I'm having difficulty in changing it to match.

考虑一个你好世界的例子,我想要这个:

Considering a hello world example, I want this:

<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>

我在这里找到了与我正在尝试做的类似的事情,但我无法获得类似的代码正确执行.(我想坚持使用 Metro 而不必更改为 cxf 或轴)

I found something similar to what I'm trying to do here but I'm having trouble getting similar code to execute properly. (I'd like to stick with Metro and not have to change to cxf or axis)

我对 JAXBContextFactory 返回一个 JAXBRIContext 看起来像这样:

My implementation of JAXBContextFactory that returns a JAXBRIContext looks like this:

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 服务的一些测试代码很简单:

Some test code for the web service is simply:

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").


Issues

In Tomcat 7.0.32 and Glassfish 3.1.2 using jaxws-rt 2.2.7 (from Maven), the above code doesn't affect my web service output (the namespace prefix is still "ns2").

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

If you started from the WSDL of the old service and generated all the various JAXB annotated request and response wrapper classes using wsimport, then in the generated package you should find a package-info.java such as

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

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

JAXB provides a mechanism for you to suggest prefix mappings on the @XmlSchema annotation, so you could try modifying package-info.java to read

@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 的自定义上下文工厂).

and see if that makes any difference to the generated messages. This also has the advantage of being pure JAXB spec (i.e. not dependent on the RI-specific custom context factory).

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

If you need to re-run wsimport you can prevent it from overwriting your modified package-info by passing the -npa option to xjc (this tells it not to generate a package-info.java but instead to put all the necessary namespace settings on the class-level annotations instead). Exactly how you do this depends how you're running 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>