我必须准备一个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>

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

最佳答案

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



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

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

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

我尝试了一下,构建进行得很好,并且相应地更新了WSDL。

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

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



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

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

这是我遵循的过程:
  • 创建自己的CustomSuffixBasedMessagesProvider,覆盖setRequestSuffix方法并删除对空后缀的检查,在isMessageElement方法中,您需要处理新的映射
  • 创建您自己的CustomSuffixBasedPortTypesProvider,覆盖setRequestSuffix方法并删除对空后缀的检查,您需要在getOperationNameisInputMessage方法中处理新的映射
  • 创建您自己的CustomWsdl11Definition作为现有DefaultWsdl11Definition的副本,并实例化在
  • 上方创建的自己的提供程序
  • 更改WebServiceConfigdefaultWsdl11Definition方法,以使用您自己的CustomWsdl11Definition来应用整个定制。

  • 但是,空后缀会带来一些挑战,因为对任何元素(每个元素都有一个空后缀)都可以。这就是为什么我提到isMessageElementisInputMessagegetOperationName处理的原因:在不断增长的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 定制类(本质上是Default的一个副本,仅实例化上述类,为简洁起见删除了导入):
    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>
    

    希望能帮助到你。这是一个实际的例子,说明了配置方面的约定能提供什么,而在框架中进行小的无法预见的更改则意味着编写代码和添加定制。

    10-08 07:00