Недопустимый wsdl, созданный spring-ws, когда элемент запроса не заканчивается на "Request"

Я должен подготовить веб-сервис для принятия уже определенной структуры wsdl anan. Я следил за учебником найти здесь, С исходным кодом для тестового проекта скачать.

для 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-файл является недействительным, и нет!--4--> указано:

<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-менее элемент отображается правильно как вход операции soap в wsdl?

3 ответов


по данным официальная Весенняя документация WS, суффикс запроса/ответа по умолчанию используется для автоматического определения запроса / ответа и как таковой генерирует ожидаемый WSDL.

DefaultWsdl11Definition, который создает WSDL из схемы XSD. Это определение повторяет все элементы элемента, найденные в схеме, и создает сообщение для всех элементов. Далее создается операция WSDL для всех сообщений с определенными суффикс запроса или ответа. Суффикс запроса по умолчанию-Request; суффикс ответа по умолчанию-Response, хотя их можно изменить, задав свойства requestSuffix и responseSuffix соответственно.

вы можете, следовательно, в указанном примере кода, изменить суффикс в WebServiceConfig настройки класса, defaultWsdl11Definition метод, добавив следующий вызов метода:

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

вы можете, например, установить его в Req вместо Request, в затем build автоматически создаст новый GetCountryReq класс, код ApplicationTests и CountryEndpoint затем необходимо будет вручную адаптировать, удалив ошибки компиляции (поскольку они все равно будут указывать на ранее существующие GetCountryRequest class), но также не забудьте изменить на @PayloadRoot аннотации CountryEndPoint класса.

я попробовал, и сборка прошла отлично, и WSDL был обновлен соответствующим образом.

речь идет об изменении суффикса по умолчанию на другой суффикс. но как насчет изменения его на пустой суффикс?

wsdl11Definition.setRequestSuffix("");

исключение: суффикс не должен быть пустым. Весна его не поддерживает. Согласно этому нить:

в принципе, проблема заключается в следующем:
Мы должны различать, какие элементы схемы являются сообщениями wsdl, а какие нет.
Из всех сообщений wsdl мы должны выяснить, какие из них являются входными (запросными) сообщениями.
Из все WSDL сообщения, мы должны выяснить, какие из них являются выходными (ответными) сообщениями.

DefaultWsdl11Definition вычисляет это суффиксами. Или, более конкретно, он делегирует SuffixBasedMessagesProvider и SuffixBasedPortTypesProvider для этого.
Поэтому, если у вас есть другой предпочтительный способ определения того, что делает сообщение ввода/вывода, вам придется написать свой собственный messagesprovider и / или porttypesprovider.

проще говоря: нет общего способ Spring-WS определить, что составляет запрос и ответ, а не использовать суффиксы.

Примечание: плакат этого сообщения был Арьен Poutsma, автор DefaultWsdl11Definition class (согласно javadocs), компонент, который обрабатывает автоматическое сопоставление на основе этих соглашений суффиксов.

но он оставляет открытую дверь: пишешь свое SuffixBasedMessagesProvider и SuffixBasedPortTypesProvider. Однако он также оставил все как личное в DefaultWsdl11Definition (где эти поставщики экземпляр), следовательно, вам также нужно будет написать (скопировать) свой собственный сопоставитель определений WSDL11.

вот процесс, которому я следовал тогда:

  • создайте свой собственный CustomSuffixBasedMessagesProvider, переопределяя setRequestSuffix метод и удаление проверки на пустой суффикс, в isMessageElement метод вам нужно будет обработать новое отображение
  • создайте свой собственный CustomSuffixBasedPortTypesProvider, переопределяя setRequestSuffix метод и удаление проверки на пустые суффикс, в getOperationName и isInputMessage методы, которые вам понадобятся для обработки нового отображения
  • создайте свой собственный CustomWsdl11Definition как копию существующего DefaultWsdl11Definition и создания экземпляра собственных поставщиков, созданных выше
  • изменить WebServiceConfig класса, defaultWsdl11Definition метод, чтобы использовать свой собственный CustomWsdl11Definition для применения всей настройки.

однако пустой суффикс поставляется с небольшим вызовом, так как это было бы хорошо для любой элемент (то есть, каждый элемент имеет пустой суффикс). Вот почему я упомянул isMessageElement, isInputMessage и getOperationName обработка: при росте WSDLs вы можете легко закончить кодирование отображения (для запроса GetCountry GetCountryResponse является ответом) или передачу свойств/карты (как предложено в нить упомянутый выше), но снова повторяя большую часть вашего 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();
    }

}

на MyWsdl11Definition пользовательский класс (по сути, копия по умолчанию, просто экземпляр классов выше, импорт удален для краткости):

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 и исправить ошибки компиляции, как указано выше (класс test и endpoint, просто не забудьте обновить аннотацию @PayloadRoot).

Build будет работать нормально. Под управлением 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>

надеюсь, что это помогает. Это реальный пример того, что соглашение по конфигурации значительно обеспечивает и что вместо этого небольшое непредвиденное изменение в рамках будет означать с точки зрения написания кода и добавления настройки.


Spring-WS автоматическая функция экспозиции wsdl основана на соглашениях, описанных в http://docs.spring.io/spring-ws/site/reference/html/server.html#server-automatic-wsdl-exposure

учебник, который вы используете в качестве отправной точки, использует аннотации вместо пространства имен, но должен быть способ указать свойства requestSuffix и responseSuffix, упомянутые в документации. Однако я боюсь, что вы не можете держать их пустыми.

в качестве альтернативы вы можете выставить вручную написанный WSDL. Я предлагаю сделать это, так как у вас есть WSDL, данный с самого начала.


есть более простой способ. Вместо 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;
    }

}