我必须准备一个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
类,然后需要手动修改ApplicationTests
和CountryEndpoint
的代码,从而消除了编译错误(因为它们仍然指向以前的版本)现有的GetCountryRequest
类),但还要确保更改localPart = "getCountryReq"
类中@PayloadRoot
批注的CountryEndPoint
属性。我尝试了一下,构建进行得很好,并且相应地更新了WSDL。
这是关于将默认后缀更改为另一个后缀。 但是如何将其更改为空后缀呢?
wsdl11Definition.setRequestSuffix("");
异常(exception):后缀不能为空。 Spring不支持它。根据此thread:
注意:此消息的发布者是
DefaultWsdl11Definition
类的作者Arjen Poutsma(根据javadocs),该类是根据这些后缀约定处理自动映射的组件。但是他留下了一扇敞开的门:编写自己的
SuffixBasedMessagesProvider
和SuffixBasedPortTypesProvider
。但是,他还在DefaultWsdl11Definition
(实例化这些提供程序)中将所有内容都保留为私有(private),因此您还需要编写(复制)自己的WSDL11定义映射器。这是我遵循的过程:
setRequestSuffix
方法并删除对空后缀的检查,在isMessageElement
方法中,您需要处理新的映射setRequestSuffix
方法并删除对空后缀的检查,您需要在getOperationName
和isInputMessage
方法中处理新的映射WebServiceConfig
类defaultWsdl11Definition
方法,以使用您自己的CustomWsdl11Definition来应用整个定制。 但是,空后缀会带来一些挑战,因为对任何元素(每个元素都有一个空后缀)都可以。这就是为什么我提到
isMessageElement
,isInputMessage
和getOperationName
处理的原因:在不断增长的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>
希望能帮助到你。这是一个实际的例子,说明了配置方面的约定能提供什么,而在框架中进行小的无法预见的更改则意味着编写代码和添加定制。