[英]Spring-ws: How to create Wsdl from an xsd with no “Request” element
[英]Invalid wsdl generated by spring-ws when the request element doesn't end with 'Request'
我必須准備一個 web 服務來接受一個已經定義的 wsdl 結構。 我按照此處找到的教程進行操作,其中包含可在此處下載的測試項目的源代碼。
對於這樣的 xsd:
<xs:element name="getCountryRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
應用程序返回的請求的wsdl操作是OK的,看起來像這樣:
<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
元素正確顯示為 wsdl 中的 soap 操作輸入?
根據Spring WS 官方文檔,Request/Response 后綴是用於自動確定請求/響應並因此生成預期 WSDL 的默認后綴。
從 XSD 架構構建 WSDL 的 DefaultWsdl11Definition。 此定義遍歷在模式中找到的所有元素元素,並為所有元素創建一條消息。 接下來,它為所有以定義的請求或響應后綴結尾的消息創建 WSDL 操作。 默認請求后綴為Request; 默認響應后綴是 Response,盡管可以分別通過設置 requestSuffix 和 responseSuffix 屬性來更改這些后綴。
因此,您可以在上述示例代碼中,更改WebServiceConfig
配置類中的后綴defaultWsdl11Definition
方法,添加以下方法調用:
wsdl11Definition.setRequestSuffix("your-new-prefix-here");
例如,您可以將其設置為Req
而不是Request
,然后構建將自動生成一個新的GetCountryReq
類,然后需要手動調整ApplicationTests
和CountryEndpoint
的代碼,消除編譯錯誤(因為它們仍然指向以前的現有的GetCountryRequest
類),但還要確保更改CountryEndPoint
類中@PayloadRoot
注釋的localPart = "getCountryReq"
屬性。
我試過了,構建很順利,WSDL 也相應地更新了。
那是關於將默認后綴更改為另一個后綴。 但是如何將其更改為空后綴呢?
wsdl11Definition.setRequestSuffix("");
例外:后綴不能為空。 春天不支持它。 根據這個線程:
基本上,問題是這樣的:
我們必須區分哪些模式元素是 wsdl 消息,哪些不是。
在所有 wsdl 消息中,我們必須找出哪些是輸入(請求)消息。
在所有 wsdl 消息中,我們必須找出哪些是輸出(響應)消息。DefaultWsdl11Definition 通過后綴解決這個問題。 或者,更具體地說,它委托給 SuffixBasedMessagesProvider 和 SuffixBasedPortTypesProvider 來執行此操作。
因此,如果您有一些其他首選方法來確定什么構成輸入/輸出消息,您將必須編寫自己的消息提供程序和/或端口類型提供程序。簡單地說:Spring-WS 沒有通用的方法來確定什么構成了請求和響應,而不是使用后綴。
注意:此消息的發布者是DefaultWsdl11Definition
類(根據 javadocs)的作者 Arjen Poutsma,該組件處理基於這些后綴約定的自動映射。
但他敞開了大門:編寫您自己的SuffixBasedMessagesProvider
和SuffixBasedPortTypesProvider
。 但是,他還在DefaultWsdl11Definition
(實例化這些提供程序的地方)中將所有內容都保留為私有,因此您還需要編寫(復制)自己的 WSDL11 定義映射器。
這是我當時遵循的過程:
setRequestSuffix
方法並刪除對空后綴的檢查,在isMessageElement
方法中您需要處理新映射setRequestSuffix
方法並刪除對空后綴的檢查,在您需要處理新映射的getOperationName
和isInputMessage
方法中WebServiceConfig
類defaultWsdl11Definition
方法,以使用您自己的 CustomWsdl11Definition 以應用整個自定義。 然而,空后綴會帶來一些挑戰,因為它適用於任何元素(也就是說,每個元素都有一個空后綴)。 這就是我提到isMessageElement
、 isInputMessage
和getOperationName
處理的原因:在增長的 WSDL 中,您可能很容易最終對映射進行硬編碼(對於 GetCountry 請求,GetCountryResponse 是響應)或傳遞屬性/映射(如上述線程中所建議的) ,但在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自定義類(本質上是默認類的副本,只是實例化上面的類,為簡潔起見刪除了導入):
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();
}
}
此外, WebServiceConfig
類的defaultWsdl11Definition
方法將更改如下(以使用上面的自定義):
@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去掉Request后綴,會生成新的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>
希望能幫助到你。 這是一個真實的例子,說明了配置約定極大地提供了什么,而在編寫代碼和添加自定義方面,框架中的一個小的不可預見的變化將意味着什么。
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;
}
}
在@EnableWs
配置類中:
@Bean(WSDL_SCHEMA_NAME)
public ProviderBasedWsdl4jDefinition providerBasedWsdl4jDefinition(XsdSchema xsdSchema) {
var wsdl4jDefinition = new ProviderBasedWsdl4jDefinition();
wsdl4jDefinition.setTargetNamespace(WSDL_TARGET_NAMESPACE);
var suffixBasedPortTypesProvider = new CustomPortTypesProvider();
suffixBasedPortTypesProvider.setPortTypeName(WSDL_PORT_TYPE_NAME);
wsdl4jDefinition.setPortTypesProvider(suffixBasedPortTypesProvider);
var inliningXsdSchemaTypesProvider = new InliningXsdSchemaTypesProvider();
inliningXsdSchemaTypesProvider.setSchema(xsdSchema);
wsdl4jDefinition.setTypesProvider(inliningXsdSchemaTypesProvider);
wsdl4jDefinition.setMessagesProvider(new CustomMessagesProvider());
var soapProvider = new SoapProvider();
soapProvider.setLocationUri(WSDL_SCHEMA_URI);
soapProvider.setServiceName(WSDL_SCHEMA_NAME);
wsdl4jDefinition.setBindingsProvider(soapProvider);
wsdl4jDefinition.setServicesProvider(soapProvider);
return wsdl4jDefinition;
}
private static class CustomMessagesProvider extends SuffixBasedMessagesProvider {
private static final String[] REQUEST_ELEMENTS =
new String[] {"requestOne", "requestTwo"};
@Override
protected boolean isMessageElement(Element element) {
if (super.isMessageElement(element)) {
return true;
} else {
boolean isElement = "element".equals(element.getLocalName()) &&
"http://www.w3.org/2001/XMLSchema".equals(element.getNamespaceURI());
String elementName = getElementName(element);
return isElement && Arrays.asList(REQUEST_ELEMENTS).contains(elementName);
}
}
}
private static class CustomPortTypesProvider extends SuffixBasedPortTypesProvider {
@Override
protected String getOperationName(Message message) {
String messageName = message.getQName().getLocalPart();
if (isInputMessage(message)) {
return messageName;
} else if (isOutputMessage(message)) {
return messageName.substring(0, messageName.length() - getResponseSuffix().length());
} else if (isFaultMessage(message)) {
return messageName.substring(0, messageName.length() - getFaultSuffix().length());
}
return null;
}
@Override
protected boolean isInputMessage(Message message) {
String messageName = message.getQName().getLocalPart();
return !messageName.endsWith(getResponseSuffix()) && !messageName.endsWith(getFaultSuffix());
}
}
我想最好的解決方案是編寫一個您需要的 WSDL 文件。 在我看來,使用除 Spring Convention 以外的請求名稱和響應名稱從 XSD 文件創建自定義 WSDL 是不可路由的。 所以最好創建 WSDL 並將其導入項目,然后從中生成您的類。
我有同樣的錯誤。 我的設置如下:
使用 mavenjaxb2-maven-plugin從現有 XSD 文件生成類
<!-- SOAP -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- The package of your generated sources -->
<packageName>com.company</packageName>
<addGeneratedAnnotation>true</addGeneratedAnnotation>
<quiet>true</quiet>
<sources>
<source>${project.basedir}/src/main/resources/file.xsd</source>
</sources>
</configuration>
</plugin>
處理程序方法:
@PayloadRoot(namespace = "URI", localPart = "LOCAL_PART")
@ResponsePayload
public JAXBElement<StatusResponse> retrieveNextStatesRequest(@RequestPayload Request request) {
// Logic
return new Response();
}
然而,jaxb2-maven-plugin不可能添加XmlRoot
注釋。 但是,它提供了一個 ObjectFactory 來生成包裝在JAXBElement
實例中的實例。
因此,當相應地更改方法時,SOAP 請求現在可以按預期工作:
@PayloadRoot(namespace = "URI", localPart = "LOCAL_PART")
@ResponsePayload
public Response retrieveNextStatesRequest(@RequestPayload Request request) {
// Logic
return new ObjectFactory.createResponse(new Response());
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.