jjzjj

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

coder 2024-03-09 原文

我必须准备一个 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 - 请求元素不以 'Request'结尾时spring-ws生成的wsdl无效的更多相关文章

    1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

      我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

    2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

      在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

    5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    6. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

      我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

    7. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

      在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

    8. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

      我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

    9. java - 等价于 Java 中的 Ruby Hash - 2

      我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

    10. ruby-on-rails - 带 Spring 锁的 Rails 4 控制台 - 2

      我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.

    随机推荐