当request-element没有以'Request'结尾时,spring-ws生成的wsdl无效

时间:2015-12-30 08:21:10

标签: java web-services wsdl cxf spring-ws

我必须准备一个web服务来接受已定义的wsdl结构。我按照教程found here,使用了测试项目downloadable here的源代码。

对于像这样的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更改为(元素名称中没有'Request')时:

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

我该如何解决?如何在wsdl中将Request - 更少的元素正确显示为soap操作输入?

4 个答案:

答案 0 :(得分:13)

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

  

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

因此,您可以在上述示例代码中更改WebServiceConfig配置类defaultWsdl11Definition方法中的后缀,添加以下方法调用:

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

例如,您可以将其设置为Req而不是Request,然后构建会自动生成新的GetCountryReq类,代码为ApplicationTests和{{然后需要手动调整1}},删除编译错误(因为它们仍然指向以前存在的CountryEndpoint类),同时确保更改GetCountryRequest的{​​{1}}属性localPart = "getCountryReq"类中的注释。

我尝试了它,构建很顺利,WSDL也相应地更新了。

关于将默认后缀更改为另一个后缀的问题。 但是如何将其更改为空后缀?

@PayloadRoot

异常:后缀不能为空。 Spring不支持它。根据这个thread

  

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

     

DefaultWsdl11Definition用后缀来计算。或者,更具体地说,它委托给SuffixBasedMessagesProvider和SuffixBasedPortTypesProvider这样做。
  因此,如果您有其他一些确定输入/输出消息的首选方法,则必须编写自己的messagesprovider和/或porttypesprovider。

     

简单地说:Spring-WS没有通用的方法来确定构成请求和响应的内容,而不是使用后缀。

注意:此消息的海报是Arjen Poutsma,CountryEndPoint类的作者(根据javadocs),该组件根据这些后缀约定处理自动映射。

但是他敞开了大门:写下你自己的wsdl11Definition.setRequestSuffix(""); DefaultWsdl11Definition。但是,他还将所有内容保留为SuffixBasedMessagesProvider中的私有(这些提供程序被实例化),因此您还需要编写(复制)自己的WSDL11定义映射器。

以下是我遵循的流程:

  • 创建您自己的CustomSuffixBasedMessagesProvider,覆盖SuffixBasedPortTypesProvider方法并删除空后缀检查,使用DefaultWsdl11Definition方法处理新映射
  • 创建您自己的CustomSuffixBasedPortTypesProvider,覆盖setRequestSuffix方法,并在处理新映射所需的isMessageElementsetRequestSuffix方法中删除空后缀检查
  • 创建您自己的CustomWsdl11Definition作为现有DefaultWsdl11Definition的副本,并实例化上面创建的您自己的提供程序
  • 更改getOperationNameisInputMessage方法,以使用您自己的CustomWsdl11Definition来应用整个自定义。

但是,空后缀带来了一些挑战,因为它适用于任何元素(也就是说,每个元素都有一个空后缀)。这就是为什么我提到WebServiceConfigdefaultWsdl11DefinitionisMessageElement处理的原因:在增长的WSDL上,您可能很容易最终对映射进行编码(对于GetCountry请求,GetCountryResponse是响应)或传递属性/地图(如上面提到的thread中所述),但在isInputMessage配置类中再次重复大部分XSD,使其难以维护,排除故障,分享。 / p>

所以,我真的建议不要采取这个旅程,要么坚持默认后缀(如果可能的话)或者创建一个新的后缀,但要避免空后缀(毕竟库不允许它们。)

但是自从我开始旅程以来,这是一个可行的解决方案:

MySuffixBasedMessagesProvider 自定义类(为简洁起见,已删除导入):

getOperationName

MySuffixBasedPortTypesProvider 自定义类(为简洁起见,已删除导入):

WebServiceConfig

MyWsdl11Definition 自定义类(基本上是默认类的副本,只是实例化上面的类,为简洁而删除了导入):

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

此外,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(); } } 类的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(); } } 方法将更改如下(使用上面的自定义):

defaultWsdl11Definition

注意WebServiceConfig有效地将后缀设置为空。

在此自定义之后,您可以更改XSD删除Request后缀,生成新的GetCountry类,您需要手动删除以前存在的GetCountryRequest并修复上面提到的编译错误(测试和端点类,只是不要忘记更新@PayloadRoot注释。

然后构建运行正常。运行@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; } main,生成的WSDL将按要求包含:

wsdl11Definition.setRequestSuffix("");

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

答案 1 :(得分:2)

Spring-WS自动wsdl曝光功能基于以下描述的约定 http://docs.spring.io/spring-ws/site/reference/html/server.html#server-automatic-wsdl-exposure

您用作起点的教程是使用注释而不是命名空间,但应该有一种方法来指定文档中提到的requestSuffix和responseSuffix属性。但是我担心你不能把它们留空。

或者,您可以公开手动编写的WSDL。我建议这样做,因为你从一开始就给出了wsdl。

答案 2 :(得分:1)

有更简单的方法。代替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;
    }

}

答案 3 :(得分:0)

我想最好的解决方案是编写您需要的WSDL文件。在我看来,从XSD文件创建带有Spring Convention以外的请求名称和响应名称的定制WSDL是不可路由的。因此,最好创建WSDL并将其导入到项目中,然后从中生成您的类。