2015-12-30 72 views
5

我必须准备一个web服务来接受anan已定义的wsdl结构。我遵循教程found here,测试项目downloadable here的源代码。当请求元素不以'请求'结尾时,由spring-ws生成的wsdl无效wldl

对于XSD像这样:

<xs:element name="getCountryRequest"> 
    <xs:complexType> 
     <xs:sequence> 
      <xs:element name="name" type="xs:string"/> 
     </xs:sequence> 
    </xs:complexType> 
</xs:element> 

WSDL操作请求,通过应用程序正常返回,看起来是这样的:

<wsdl:binding name="CountriesPortSoap11" type="tns:CountriesPort"> 
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> 
    <wsdl:operation name="getCountry"> 
     <soap:operation soapAction=""/> 
     <wsdl:input name="getCountryRequest"> 
      <soap:body use="literal"/> 
     </wsdl:input> 
     <wsdl:output name="getCountryResponse"> 
      <soap:body use="literal"/> 
     </wsdl:output> 
    </wsdl:operation> 
</wsdl:binding> 

但是,当我改变XSD在(没有 '请求'元素名称):

<xs:element name="getCountry"> 
    <xs:complexType> 
     <xs:sequence> 
      <xs:element name="name" type="xs:string"/> 
     </xs:sequence> 
    </xs:complexType> 
</xs:element> 

的wsdl是无效的,并且没有指定<input>

<wsdl:binding name="CountriesPortSoap11" type="tns:CountriesPort"> 
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> 
    <wsdl:operation name="getCountry"> 
     <soap:operation soapAction=""/> 
     <wsdl:output name="getCountryResponse"> 
      <soap:body use="literal"/> 
     </wsdl:output> 
    </wsdl:operation> 
</wsdl:binding> 

我该如何解决这个问题?我怎样才能使Request -less元素在wsdl中作为肥皂操作输入正确显示?

+0

对于大家谁是有类似的问题:使用org.apache.cxf并且从端点带有wsdl2java的WSDL。它更简单,而且lib实际上允许在同一端点上同时使用soap 1.1和1.2。 – Dariusz

回答

11

根据official Spring WS documentation,请求/响应后缀是用于自动确定请求/响应的默认值,因此会生成预期的WSDL。

DefaultWsdl11Definition从XSD模式构建WSDL。该定义迭代架构中找到的所有元素元素,并为所有元素创建一条消息。接下来,它为所有以定义的请求或响应后缀结尾的消息创建WSDL操作。默认的请求后缀是Request;默认的响应后缀是Response,尽管这些可以通过分别设置requestSuffix和responseSuffix属性来更改。

可以因此,在所提及的例子代码,在WebServiceConfig配置类,defaultWsdl11Definition方法更改后缀,加入如下方法调用:

wsdl11Definition.setRequestSuffix("your-new-prefix-here"); 

可以,例如,将其设置为Req而不是Request,构建会自动生成一个新的GetCountryReq类,然后需要手动修改ApplicationTestsCountryEndpoint的代码,从而消除编译错误(因为它们仍然指向先前存在的GetCountryRequest类),但也要确保更改CountryEndPoint类中的@PayloadRoot注释的localPart = "getCountryReq"属性。

我试过了,构建得很好,WSDL也相应地更新了。

这是关于将默认后缀更改为另一个后缀。 但是把它改为空后缀呢?

wsdl11Definition.setRequestSuffix(""); 

例外:后缀不能为空。 Spring不支持它。根据这一thread

基本上,问题是这样的:
我们必须区分哪些架构元素是WSDL消息,而哪些不是。
在所有wsdl消息中,我们必须找出哪些是输入(请求)消息。
在所有wsdl消息中,我们必须找出哪些是输出(响应)消息。

DefaultWsdl11Definition通过后缀计算出来。或者,更具体地说,它委托给一个SuffixBasedMessagesProvider和SuffixBasedPortTypesProvider来做到这一点。
因此,如果您有其他一些首选的方法来确定输入/输出消息的内容,则必须编写自己的消息提供程序和/或porttypesprovider。

简而言之:Spring-WS没有通用的方法来确定构成请求和响应的是什么,而不是使用后缀。

注:此消息的海报是罗保茨马,所述DefaultWsdl11Definition类的作者(根据Javadoc中),它处理基于这些后缀公约自动映射的组件。

但他离开了一扇敞开的大门:写你自己的SuffixBasedMessagesProviderSuffixBasedPortTypesProvider。但是,他也将所有内容都保留为DefaultWsdl11Definition(这些提供程序实例化的地方),因此您还需要编写(复制)您自己的WSDL11定义映射程序。

这里是我跟着那么过程:

  • 创建自己的CustomSuffixBasedMessagesProvider,覆盖setRequestSuffix方法和取消对空后缀的检查,在isMessageElement方法,你需要处理的新的映射
  • 创建您自己的CustomSuffixBasedPortTypesProvider,覆盖setRequestSuffix方法并取消对空后缀的检查,在getOperationNameisInputMessage方法中您将需要处理新映射
  • 创建自己的CustomWsdl11Definition作为现有DefaultWsdl11Definition的副本和实例上述
  • 更改WebServiceConfig类,defaultWsdl11Definition方法创建了自己的供应商,使用自己的CustomWsdl11Definition为了应用全定制。

然而,空后缀带有一些挑战,因为它可以适用于任何元素(也就是说,每个元素都有一个空的后缀)。这就是为什么我提到了isMessageElement,isInputMessagegetOperationName的处理:在增长的WSDL上,您可能很容易结束对代码进行编码(对于GetCountry请求,GetCountryResponse是响应)或传递属性/映射(如上面提到的thread中所建议的) ,但在您的WebServiceConfig配置类中再次重复大部分XSD,因此难以维护,故障排除和共享。因此,我真的建议不要采取这一旅程,并坚持默认后缀(如果可能)或创建一个新的,但避免空后缀(毕竟,它们不被图书馆允许)。

但自从我采取了旅程,这里是工作的解决方案:

MySuffixBasedMessagesProvider自定义类(进口简洁,删除):

package hello; 

public class MySuffixBasedMessagesProvider extends SuffixBasedMessagesProvider { 

    protected String requestSuffix = DEFAULT_REQUEST_SUFFIX; 

    public String getRequestSuffix() { 
     return this.requestSuffix; 
    } 

    public void setRequestSuffix(String requestSuffix) { 
     this.requestSuffix = requestSuffix; 
    } 

    @Override 
    protected boolean isMessageElement(Element element) { 
     if (isMessageElement0(element)) { 
      String elementName = getElementName(element); 
      Assert.hasText(elementName, "Element has no name"); 
      return elementName.endsWith(getResponseSuffix()) 
        || (getRequestSuffix().isEmpty() ? true : elementName.endsWith(getRequestSuffix())) 
        || elementName.endsWith(getFaultSuffix()); 
     } 
     return false; 
    } 

    protected boolean isMessageElement0(Element element) { 
     return "element".equals(element.getLocalName()) 
       && "http://www.w3.org/2001/XMLSchema".equals(element.getNamespaceURI()); 
    } 
} 

MySuffixBasedPortTypesProvider自定义类(进口去除为简洁起见):

package hello; 

public class MySuffixBasedPortTypesProvider extends SuffixBasedPortTypesProvider { 

    private String requestSuffix = DEFAULT_REQUEST_SUFFIX; 

    public String getRequestSuffix() { 
     return requestSuffix; 
    } 

    public void setRequestSuffix(String requestSuffix) { 
     this.requestSuffix = requestSuffix; 
    } 

    @Override 
    protected String getOperationName(Message message) { 
     String messageName = getMessageName(message); 
     String result = null; 
     if (messageName != null) { 
      if (messageName.endsWith(getResponseSuffix())) { 
       result = messageName.substring(0, messageName.length() - getResponseSuffix().length()); 
      } else if (messageName.endsWith(getFaultSuffix())) { 
       result = messageName.substring(0, messageName.length() - getFaultSuffix().length()); 
      } else if (messageName.endsWith(getRequestSuffix())) { 
       result = messageName.substring(0, messageName.length() - getRequestSuffix().length()); 
      } 
     } 
     return result; 
    } 

    @Override 
    protected boolean isInputMessage(Message message) { 
     String messageName = getMessageName(message); 

     return messageName != null && !messageName.endsWith(getResponseSuffix()); 
    } 

    private String getMessageName(Message message) { 
     return message.getQName().getLocalPart(); 
    } 

} 

MyWsdl11Definition自定义类(本质上是一个默认的一个副本,只是实例以上的类,进口为简洁移除):

package hello; 

public class MyWsdl11Definition implements Wsdl11Definition, InitializingBean { 

    private final InliningXsdSchemaTypesProvider typesProvider = new InliningXsdSchemaTypesProvider(); 

    private final SuffixBasedMessagesProvider messagesProvider = new MySuffixBasedMessagesProvider(); 

    private final SuffixBasedPortTypesProvider portTypesProvider = new MySuffixBasedPortTypesProvider(); 

    private final SoapProvider soapProvider = new SoapProvider(); 

    private final ProviderBasedWsdl4jDefinition delegate = new ProviderBasedWsdl4jDefinition(); 

    private String serviceName; 

    public MyWsdl11Definition() { 
     delegate.setTypesProvider(typesProvider); 
     delegate.setMessagesProvider(messagesProvider); 
     delegate.setPortTypesProvider(portTypesProvider); 
     delegate.setBindingsProvider(soapProvider); 
     delegate.setServicesProvider(soapProvider); 
    } 

    public void setTargetNamespace(String targetNamespace) { 
     delegate.setTargetNamespace(targetNamespace); 
    } 

    public void setSchema(XsdSchema schema) { 
     typesProvider.setSchema(schema); 
    } 

    public void setSchemaCollection(XsdSchemaCollection schemaCollection) { 
     typesProvider.setSchemaCollection(schemaCollection); 
    } 

    public void setPortTypeName(String portTypeName) { 
     portTypesProvider.setPortTypeName(portTypeName); 
    } 

    public void setRequestSuffix(String requestSuffix) { 
     portTypesProvider.setRequestSuffix(requestSuffix); 
     messagesProvider.setRequestSuffix(requestSuffix); 
    } 

    public void setResponseSuffix(String responseSuffix) { 
     portTypesProvider.setResponseSuffix(responseSuffix); 
     messagesProvider.setResponseSuffix(responseSuffix); 
    } 

    public void setFaultSuffix(String faultSuffix) { 
     portTypesProvider.setFaultSuffix(faultSuffix); 
     messagesProvider.setFaultSuffix(faultSuffix); 
    } 

    public void setCreateSoap11Binding(boolean createSoap11Binding) { 
     soapProvider.setCreateSoap11Binding(createSoap11Binding); 
    } 

    public void setCreateSoap12Binding(boolean createSoap12Binding) { 
     soapProvider.setCreateSoap12Binding(createSoap12Binding); 
    } 

    public void setSoapActions(Properties soapActions) { 
     soapProvider.setSoapActions(soapActions); 
    } 

    public void setTransportUri(String transportUri) { 
     soapProvider.setTransportUri(transportUri); 
    } 

    public void setLocationUri(String locationUri) { 
     soapProvider.setLocationUri(locationUri); 
    } 

    public void setServiceName(String serviceName) { 
     soapProvider.setServiceName(serviceName); 
     this.serviceName = serviceName; 
    } 

    @Override 
    public void afterPropertiesSet() throws Exception { 
     if (!StringUtils.hasText(delegate.getTargetNamespace()) && typesProvider.getSchemaCollection() != null && 
       typesProvider.getSchemaCollection().getXsdSchemas().length > 0) { 
      XsdSchema schema = typesProvider.getSchemaCollection().getXsdSchemas()[0]; 
      setTargetNamespace(schema.getTargetNamespace()); 
     } 
     if (!StringUtils.hasText(serviceName) && StringUtils.hasText(portTypesProvider.getPortTypeName())) { 
      soapProvider.setServiceName(portTypesProvider.getPortTypeName() + "Service"); 
     } 
     delegate.afterPropertiesSet(); 
    } 

    @Override 
    public Source getSource() { 
     return delegate.getSource(); 
    } 

} 

此外,WebServiceConfig类的defaultWsdl11Definition方法将如下改变(至使用上面的定制):

@Bean(name = "countries") 
public Wsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) { 
    MyWsdl11Definition wsdl11Definition = new MyWsdl11Definition(); 
    wsdl11Definition.setPortTypeName("CountriesPort"); 
    wsdl11Definition.setLocationUri("/ws"); 
    wsdl11Definition.setRequestSuffix(""); 
    wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-producing-web-service"); 
    wsdl11Definition.setSchema(countriesSchema); 
    return wsdl11Definition; 
} 

注意wsdl11Definition.setRequestSuffix("");其有效地设置了后缀在空。

在这个定制之后,您可以更改XSD删除请求后缀,将生成新的GetCountry类,您需要手动删除先前存在的GetCountryRequest并修复上面提到的编译错误(测试和端点类,不要忘记更新@PayloadRoot注释)。

构建会运行良好。运行Application main,生成的WSDL将包含请求:

<wsdl:binding name="CountriesPortSoap11" type="tns:CountriesPort"> 
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> 
<wsdl:operation name="getCountry"> 
    <soap:operation soapAction=""/> 
    <wsdl:input name="getCountry"> 
    <soap:body use="literal"/> 
    </wsdl:input> 
    <wsdl:output name="getCountryResponse"> 
    <soap:body use="literal"/> 
    </wsdl:output> 
</wsdl:operation> 
</wsdl:binding> 

希望它有帮助。这是配置约定大大提供的一个真实例子,而在框架中的一个小的无法预料的变化将意味着编写代码和添加自定义。

0

有一种更简单的方法。在地方DefaultWsdl11Definition的使用ProviderBasedWsdl4jDefinition并设置所有的默认提供商只有一个 - 在地方SuffixBasedPortTypesProvider的使用是这样的:

public class DelphiStylePortTypesProvider extends AbstractPortTypesProvider { 

    @Override 
    protected String getOperationName(Message msg) { 
     if (isInputMessage(msg)) { 
      return msg.getQName().getLocalPart(); 
     } 
     if (isOutputMessage(msg)) { 
      return msg.getQName().getLocalPart().replace("Response", ""); 
     } 
     return ""; 
    } 

    @Override 
    protected boolean isInputMessage(Message msg) { 
     return !msg.getQName().getLocalPart().endsWith("Response"); 
    } 

    @Override 
    protected boolean isOutputMessage(Message msg) { 
     return msg.getQName().getLocalPart().endsWith("Response"); 
    } 

    @Override 
    protected boolean isFaultMessage(Message msg) { 
     return false; 
    } 

}