java - 请求元素不以 'Request'结尾时spring-ws生成的wsdl无效

标签 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 更改为(元素名称中没有“请求”)时:
<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 中的肥皂操作输入?

最佳答案

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

The DefaultWsdl11Definition which builds a WSDL from a XSD schema. This definition iterates over all element elements found in the schema, and creates a message for all elements. Next, it creates WSDL operation for all messages that end with the defined request or response suffix. The default request suffix is Request; the default response suffix is Response, though these can be changed by setting the requestSuffix and responseSuffix properties, respectively.



因此,您可以在上述示例代码中更改 WebServiceConfig 中的后缀。配置类,defaultWsdl11Definition方法,添加以下方法调用:
wsdl11Definition.setRequestSuffix("your-new-prefix-here");

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

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

这是关于将默认后缀更改为另一个后缀。 但是将其更改为空后缀呢?
wsdl11Definition.setRequestSuffix("");

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

Basically, the problem is this:
We have to differentiate which schema elements are wsdl messages, and which aren't.
Of all wsdl messages, we have to figure out which are input (request) messages.
Of all wsdl messages, we have to figure out which are output (response) messages.

The DefaultWsdl11Definition figures this out by suffixes. Or, more specifically, it delegates to a SuffixBasedMessagesProvider and SuffixBasedPortTypesProvider to do so.
So if you have some other preferred way of determining what makes an input/output message, you will have to write your own messagesprovider and or porttypesprovider.

Simply put: there is no generic way for Spring-WS to determine what makes up a request and a response, rather than using suffixes.



注意:此消息的发布者是 DefaultWsdl11Definition 的作者 Arjen Poutsma类(根据 javadocs),处理基于这些后缀约定的自动映射的组件。

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

这是我当时遵循的过程:
  • 创建您自己的 CustomSuffixBasedMessagesProvider,覆盖 setRequestSuffix方法并删除对空后缀的检查,在 isMessageElement 中您需要处理新映射的方法
  • 创建您自己的 CustomSuffixBasedPortTypesProvider,覆盖 setRequestSuffix方法并删除对空后缀的检查,在 getOperationName 中和 isInputMessage处理新映射所需的方法
  • 创建您自己的 CustomWsdl11Definition 作为现有 DefaultWsdl11Definition 的副本并实例化您在上面创建的自己的提供程序
  • 更改 WebServiceConfig类(class),defaultWsdl11Definition方法,使用您自己的 CustomWsdl11Definition 以应用整个自定义。

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

    所以,我真的建议不要走这条路,要么坚持使用默认后缀(如果可能),要么创建一个新后缀,但要避免空后缀(毕竟图书馆不允许)。

    但自从我踏上旅程以来,这里是可行的解决方案:

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

    MyWsdl11定义自定义类(本质上是默认类的副本,只是实例化上面的类,为简洁起见删除了导入):
    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的方法类将更改如下(使用上面的自定义):
    @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>
    

    希望能帮助到你。这是一个真实的例子,说明了配置上的约定极大地提供了什么,而框架中的一个小的不可预见的变化在编写代码和添加自定义方面意味着什么。

    关于java - 请求元素不以 'Request'结尾时spring-ws生成的wsdl无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34525587/

    相关文章:

    java - 从渐进列表中选择不重复的随机数

    c# - ASP.NET Web 服务 - 如何使用连接字符串将其连接到数据库?

    java - 我如何将图像对象从 php 发送到 Java webservice

    java - 在 Android 中使用 FlightAware API

    web-services - Web 方法返回值的良好设计模式是什么?

    Java GUI Windows 根本不出现

    java - 在 do ... while() 评估中处理 try/catch 异常的最佳方法?

    java - 无法从 MapReduce 代码访问 HBase

    java - 将字符串从 xml 响应转换为 pdf

    sql-server - ColdFusion 10 serializeJSON 将是/否字符串转换为 bool 值 - 如何阻止它?