我必须准备一个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>
我该如何解决?如何在wsdl中将Request
- 更少的元素正确显示为soap操作输入?
答案 0 :(得分:13)
根据official Spring WS documentation,请求/响应后缀是用于自动确定请求/响应的默认值,因此生成预期的WSDL。
DefaultWsdl11Definition,它从XSD架构构建WSDL。此定义迭代在架构中找到的所有元素元素,并为所有元素创建消息。接下来,它为以定义的请求或响应后缀结尾的所有消息创建WSDL操作。默认请求后缀为Request;默认响应后缀是Response,尽管可以通过分别设置requestSuffix和responseSuffix属性来更改它们。
因此,您可以在上述示例代码中更改WebServiceConfig
配置类defaultWsdl11Definition
方法中的后缀,添加以下方法调用:
wsdl11Definition.setRequestSuffix("your-new-prefix-here");
例如,您可以将其设置为Req
而不是Request
,然后构建会自动生成新的GetCountryReq
类,代码为ApplicationTests
和{{然后需要手动调整1}},删除编译错误(因为它们仍然指向以前存在的CountryEndpoint
类),同时确保更改GetCountryRequest
的{{1}}属性localPart = "getCountryReq"
类中的注释。
我尝试了它,构建很顺利,WSDL也相应地更新了。
关于将默认后缀更改为另一个后缀的问题。 但是如何将其更改为空后缀?
@PayloadRoot
异常:后缀不能为空。 Spring不支持它。根据这个thread:
基本上,问题是:
我们必须区分哪些架构元素是wsdl消息,哪些是不是。
在所有wsdl消息中,我们必须弄清楚哪些是输入(请求)消息 在所有wsdl消息中,我们必须弄清楚哪些是输出(响应)消息。DefaultWsdl11Definition用后缀来计算。或者,更具体地说,它委托给SuffixBasedMessagesProvider和SuffixBasedPortTypesProvider这样做。
因此,如果您有其他一些确定输入/输出消息的首选方法,则必须编写自己的messagesprovider和/或porttypesprovider。简单地说:Spring-WS没有通用的方法来确定构成请求和响应的内容,而不是使用后缀。
注意:此消息的海报是Arjen Poutsma,CountryEndPoint
类的作者(根据javadocs),该组件根据这些后缀约定处理自动映射。
但是他敞开了大门:写下你自己的wsdl11Definition.setRequestSuffix("");
和DefaultWsdl11Definition
。但是,他还将所有内容保留为SuffixBasedMessagesProvider
中的私有(这些提供程序被实例化),因此您还需要编写(复制)自己的WSDL11定义映射器。
以下是我遵循的流程:
SuffixBasedPortTypesProvider
方法并删除空后缀检查,使用DefaultWsdl11Definition
方法处理新映射setRequestSuffix
方法,并在处理新映射所需的isMessageElement
和setRequestSuffix
方法中删除空后缀检查getOperationName
类isInputMessage
方法,以使用您自己的CustomWsdl11Definition来应用整个自定义。但是,空后缀带来了一些挑战,因为它适用于任何元素(也就是说,每个元素都有一个空后缀)。这就是为什么我提到WebServiceConfig
,defaultWsdl11Definition
和isMessageElement
处理的原因:在增长的WSDL上,您可能很容易最终对映射进行编码(对于GetCountry请求,GetCountryResponse是响应)或传递属性/地图(如上面提到的thread中所述),但在isInputMessage
配置类中再次重复大部分XSD,使其难以维护,排除故障,分享。 / p>
所以,我真的建议不要采取这个旅程,要么坚持默认后缀(如果可能的话)或者创建一个新的后缀,但要避免空后缀(毕竟库不允许它们。)
但是自从我开始旅程以来,这是一个可行的解决方案:
MySuffixBasedMessagesProvider 自定义类(为简洁起见,已删除导入):
getOperationName
MySuffixBasedPortTypesProvider 自定义类(为简洁起见,已删除导入):
WebServiceConfig
MyWsdl11Definition 自定义类(基本上是默认类的副本,只是实例化上面的类,为简洁而删除了导入):
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());
}
}
此外,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();
}
}
类的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
有效地将后缀设置为空。
在此自定义之后,您可以更改XSD删除Request后缀,生成新的GetCountry类,您需要手动删除以前存在的GetCountryRequest并修复上面提到的编译错误(测试和端点类,只是不要忘记更新@PayloadRoot注释。
然后构建运行正常。运行@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;
}
main,生成的WSDL将按要求包含:
wsdl11Definition.setRequestSuffix("");
希望它有所帮助。这是一个关于配置的约定大大提供的一个真实的例子,而在框架编写代码和添加自定义方面,框架中的一个小的无法预料的变化将是什么意思。
答案 1 :(得分:2)
Spring-WS自动wsdl曝光功能基于以下描述的约定 http://docs.spring.io/spring-ws/site/reference/html/server.html#server-automatic-wsdl-exposure
您用作起点的教程是使用注释而不是命名空间,但应该有一种方法来指定文档中提到的requestSuffix和responseSuffix属性。但是我担心你不能把它们留空。
或者,您可以公开手动编写的WSDL。我建议这样做,因为你从一开始就给出了wsdl。
答案 2 :(得分:1)
有更简单的方法。代替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;
}
}
答案 3 :(得分:0)
我想最好的解决方案是编写您需要的WSDL文件。在我看来,从XSD文件创建带有Spring Convention以外的请求名称和响应名称的定制WSDL是不可路由的。因此,最好创建WSDL并将其导入到项目中,然后从中生成您的类。