当request元素未以'Request'结尾时,spring-ws生成的wsdl无效 [英] Invalid wsdl generated by spring-ws when the request element doesn't end with 'Request'

查看:239
本文介绍了当request元素未以'Request'结尾时,spring-ws生成的wsdl无效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须准备一个Web服务以接受已经定义的wsdl结构。我遵循了在此处找到的指南,其中包含测试项目的源代码可从此处下载

I must prepare a webservice to accept anan already defined wsdl structure. I followed the tutorial found here, with source code for the test project 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 operation for request returned by application is OK, looks like this:

<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)时:

But when I change the xsd to (no 'Request' in element name):

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

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

the wsdl is invalid, and has no <input> specified:

<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-less元素正确显示为wsdl中的soap操作输入?

How can I fix that? How can I make a Request-less element appear properly as soap operation input in wsdl?

推荐答案

根据 Spring WS官方文档, Request / Response后缀是用于自动确定请求/响应并因此生成预期WSDL的默认后缀。

According to official Spring WS documentation, the Request/Response suffix are the default ones used to automatically determine request/response and as such generate the expected WSDL.


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

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 方法,添加以下方法调用: / p>

You can hence, in the mentioned example code, change the suffix in the WebServiceConfig configuration class, defaultWsdl11Definition method, adding the following method invocation:

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

例如,您可以将其设置为 Req 而不是 Request ,该版本将自动生成一个新的 GetCountryReq 类,代码为 ApplicationTests 和 CountryEndpoint ,消除编译错误(因为它们仍指向以前存在的 GetCountryRequest 类),还请确保更改 @PayloadRoot localPart = getCountryReq 属性 CountryEndPoint 类中的c $ c>注释。

You can, for instance, set it to Req instead of Request, the build will then automatically generate a new GetCountryReq class, code of ApplicationTests and CountryEndpoint would then need to be manually adapted, removing compilation errors (as they would still point to the previously existing GetCountryRequest class) but also making sure to change the localPart = "getCountryReq" attribute of the @PayloadRoot annotation in the CountryEndPoint class.

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

I tried it and build went fine and WSDL was updated accordingly.

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

That's about changing the default suffix to another suffix. But what about changing it to an empty suffix?

wsdl11Definition.setRequestSuffix("");

例外:后缀不能为空。 Spring不支持它。根据此线程

Exception: suffix must not be empty. Spring does not support it. According to this thread:


基本上,问题是这样的:

我们必须区分哪些架构元素是wsdl消息,哪些不是。

在所有wsdl消息中,我们必须找出哪些是输入(请求)消息。

在所有wsdl消息中,我们必须确定输出(响应)消息。

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.

DefaultWsdl11Definition通过后缀来解决。或者,更具体地说,它委托给SuffixBasedMessagesProvider和SuffixBasedPortTypesProvider进行。

因此,如果您有其他确定输入/输出消息的方式,则必须编写自己的messageprovider和

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.

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

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

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

Note: the poster of this message was Arjen Poutsma, author of the DefaultWsdl11Definition class (according to javadocs), the component which handles the automatic mapping based on these suffix conventions.

但是他留下了门户:编写自己的 SuffixBasedMessagesProvider SuffixBasedPortTypesProvider 。但是,他还把所有内容都保留在 DefaultWsdl11Definition (实例化这些提供程序的位置)中,因此,您还需要编写(复制)自己的WSDL11定义映射器。

But he leaves an open door: writing your own SuffixBasedMessagesProvider and SuffixBasedPortTypesProvider. However, he also left everything as private in the DefaultWsdl11Definition (where these providers are instantiated), hence you would also need to write (copy) your own WSDL11 definition mapper.

这是我遵循的过程,


  • 创建您自己的CustomSuffixBasedMessagesProvider,覆盖 setRequestSuffix 方法并删除对空后缀的检查,在 isMessageElement 方法中,您需要处理新的映射

  • 创建您自己的CustomSuffixBasedPortTypesProvider,覆盖 setRequestSuffix 方法并在 getOperationName 中删除对空后缀的检查code>和 isInputMessage 方法,您将需要处理新的映射

  • 创建自己的CustomWsdl11Definition作为现有副本的副本DefaultWsdl11Definition并实例化上面创建的自己的提供程序

  • 更改 WebServiceConfig 类, defaultWsdl11Definition 方法,以使用您自己的CustomWsdl11Definition来应用整个定制。

  • Create your own CustomSuffixBasedMessagesProvider, overriding the setRequestSuffix method and removing the check on empty suffix, in the isMessageElement method you would need to handle the new mapping
  • Create your own CustomSuffixBasedPortTypesProvider, overriding the setRequestSuffix method and removing the check on empty suffix, in the getOperationName and isInputMessage methods you would need to handle the new mapping
  • Create your own CustomWsdl11Definition as a copy of the existing DefaultWsdl11Definition and instantiating your own providers created above
  • Change the WebServiceConfig class, defaultWsdl11Definition method, to use your own CustomWsdl11Definition in order to apply the whole customization.

但是,空后缀会带来一些挑战,因为它对任何元素都适用(也就是说,每个元素都有一个空后缀)。这就是为什么我提到 isMessageElement isInputMessage getOperationName 处理的原因:在不断增长的WSDL上,您可能很容易陷入对映射的编码(对于GetCountry请求,GetCountryResponse是响应)或传递属性/映射(如线程),但再次在其中重复了大部分XSD您的 WebServiceConfig 配置类,使其难以维护,排除故障和共享。

However, empty suffix comes with a bit of challenge, since it would be fine for any element (that is, every element has an empty suffix). That's why I mentioned the isMessageElement, isInputMessage and getOperationName handling: on growing WSDLs, you may easily end up on harcoding the mapping (for GetCountry request, GetCountryResponse is the response) or passing a properties/map (as suggested in the thread mentioned above), but again repeating most of your XSD again in your WebServiceConfig configuration class, making it hard to maintain, troubleshoot, share.

因此,我真的建议不要进行此操作,或者坚持使用默认的后缀(如果可能),或者创建一个新的后缀,但避免使用空的后缀(毕竟,库不允许这些后缀)。

So, I would really suggest not to take this journey and either stick to the default suffix (if possible) or create a new one, but avoid empty suffix (they are not allowed by the library after all).

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

But since I took the journey, here is the working solution:

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

The MySuffixBasedMessagesProvider custom class (imports removed for brevity):

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 自定义类(为简便起见,删除了导入内容):

The MySuffixBasedPortTypesProvider custom class (imports removed for brevity):

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类的副本,只是实例化上述类,为简洁起见删除了导入):

The MyWsdl11Definition custom class (essentially a copy of the Default one, just instantiating the classes above, imports removed for brevity):

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

}

此外,<$ c $ WebServiceConfig 类的c> defaultWsdl11Definition 方法将更改如下(以使用上面的自定义):

Furthermore, the defaultWsdl11Definition method of the WebServiceConfig class would change as following (to use the customization above):

@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(); 可以将后缀有效地设置为空。

Note the wsdl11Definition.setRequestSuffix(""); which effectively sets the suffix at empty.

此定制后,您可以更改XSD并删除请求后缀,然后将生成新的GetCountry类,您需要手动删除以前存在的GetCountryRequest并进行修复如上所述的编译错误(测试和终结点类,也请不要忘记也更新@PayloadRoot批注)。

After this customization, you can then change the XSD removing the Request suffix, the new GetCountry class would be generated, you would need to manually remove the previously existing GetCountryRequest and fix the compilation errors as mentioned above (test and endpoint class, just don't forget to update the @PayloadRoot annotation as well).

构建将运行正常。运行 Application 主程序,然后生成的WSDL将按要求包含:

Build would then run fine. Running the Application main, the generated WSDL would then contain as requested:

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

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

Hope it helps. This is a real example of what convention over configuration greatly provides and what instead a small unforeseen change in a framework would then mean in terms of writing code and adding customization.

这篇关于当request元素未以'Request'结尾时,spring-ws生成的wsdl无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆