Spring-WS如何在无Request后缀时生成含输入的SOAP服务
问题
需要基于供应商提供的行业标准XSD生成SOAP Web服务(消费者仅支持SOAP),XSD中输入输出元素定义如下:
<xsd:element name="getData"> <xsd:complexType> <xsd:sequence> <xsd:element name="Request" type="CT_getDataRequest" minOccurs="1" maxOccurs="1"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="getDataResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="Response" type="CT_getDataResponse" minOccurs="1" maxOccurs="1"/> </xsd:sequence> </xsd:complexType> </xsd:element>
Spring-WS端点和配置代码如下:
端点代码
@Endpoint @RequiredArgsConstructor public class DataEndpoint { private static final String NAMESPACE_URI = "http://www.machine-config.net/namespace/data"; @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getData") @ResponsePayload public GetDataResponse getData(@RequestPayload GetData request) { // 业务逻辑实现 } }
WSDL配置代码
@EnableWs @Configuration public class WebServiceConfig extends WsConfigurerAdapter { @Bean public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) { MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet(); messageDispatcherServlet.setApplicationContext(applicationContext); messageDispatcherServlet.setTransformWsdlLocations(true); return new ServletRegistrationBean(messageDispatcherServlet, "/ws/*"); } @Bean(name = "dataDefinition") public DefaultWsdl11Definition dataWsdl11Definition() { DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); wsdl11Definition.setPortTypeName("dataServicePortType"); wsdl11Definition.setLocationUri("/ws"); wsdl11Definition.setTargetNamespace("http://www.machine-config.net/namespace"); wsdl11Definition.setSchemaCollection(dataSchemas()); return wsdl11Definition; } @Bean(name = "dataSchema") public XsdSchemaCollection dataSchemas() { CommonsXsdSchemaCollection xsds = new CommonsXsdSchemaCollection(new ClassPathResource("xsd/data-2.18.1433.2.xsd")); xsds.setUriResolver(new DefaultURIResolver()); xsds.setInline(true); return xsds; } }
生成的WSDL中getData操作仅包含输出部分,缺失输入:
<wsdl:operation name="getData"> <wsdl:output message="sch0:getDataResponse" name="getDataResponse"> </wsdl:output> </wsdl:operation>
已知修改XSD将getData改为getDataRequest可正常生成输入,但无法修改供应商提供的XSD,需解决Spring-WS识别无Request后缀输入参数的问题。
解决方案
Spring-WS的DefaultWsdl11Definition默认通过*Request/*Response后缀匹配生成WSDL操作,因此需要自定义操作映射逻辑,以下两种方法均可解决:
方法1:手动添加WSDL操作
直接在DefaultWsdl11Definition配置中手动指定getData操作的输入输出消息,精准匹配XSD中的元素:
修改dataWsdl11Definition方法:
@Bean(name = "dataDefinition") public DefaultWsdl11Definition dataWsdl11Definition(XsdSchemaCollection dataSchemas) { DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); wsdl11Definition.setPortTypeName("dataServicePortType"); wsdl11Definition.setLocationUri("/ws"); wsdl11Definition.setTargetNamespace("http://www.machine-config.net/namespace"); wsdl11Definition.setSchemaCollection(dataSchemas); // 手动构建getData操作,指定输入输出的QName String inputNamespace = "http://www.machine-config.net/namespace/data"; QName inputQName = new QName(inputNamespace, "getData"); QName outputQName = new QName(inputNamespace, "getDataResponse"); WsdlOperationDefinition getDataOperation = new WsdlOperationDefinition("getData"); getDataOperation.setInput(new WsdlInputDefinition(inputQName)); getDataOperation.setOutput(new WsdlOutputDefinition(outputQName)); wsdl11Definition.getOperations().add(getDataOperation); return wsdl11Definition; }
方法2:自定义操作构建器
如果存在多个类似的无Request后缀的操作,可自定义OperationBuilder实现通用匹配逻辑(自动匹配XXX和XXXResponse元素对):
- 创建自定义操作构建器类:
import org.springframework.ws.wsdl.wsdl11.DefaultOperationBuilder; import org.springframework.ws.wsdl.wsdl11.WsdlOperationDefinition; import org.springframework.ws.wsdl.wsdl11.WsdlInputDefinition; import org.springframework.ws.wsdl.wsdl11.WsdlOutputDefinition; import org.springframework.ws.xml.xsd.XsdSchema; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.List; import java.util.Map; public class CustomOperationBuilder extends DefaultOperationBuilder { @Override protected List<WsdlOperationDefinition> buildOperations(XsdSchema schema) { List<WsdlOperationDefinition> operations = new ArrayList<>(); Map<QName, ?> elements = schema.getElementDefinitions(); for (Map.Entry<QName, ?> entry : elements.entrySet()) { QName elementQName = entry.getKey(); String elementName = elementQName.getLocalPart(); // 跳过Response后缀的元素,只处理请求元素 if (!elementName.endsWith("Response")) { // 查找对应的Response元素 QName responseQName = new QName(elementQName.getNamespaceURI(), elementName + "Response"); if (elements.containsKey(responseQName)) { // 构建操作并添加到列表 WsdlOperationDefinition operation = new WsdlOperationDefinition(elementName); operation.setInput(new WsdlInputDefinition(elementQName)); operation.setOutput(new WsdlOutputDefinition(responseQName)); operations.add(operation); } } } return operations; } }
- 在WSDL配置中使用自定义构建器:
@Bean(name = "dataDefinition") public DefaultWsdl11Definition dataWsdl11Definition() { DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); wsdl11Definition.setPortTypeName("dataServicePortType"); wsdl11Definition.setLocationUri("/ws"); wsdl11Definition.setTargetNamespace("http://www.machine-config.net/namespace"); wsdl11Definition.setSchemaCollection(dataSchemas()); // 设置自定义操作构建器 wsdl11Definition.setOperationBuilder(new CustomOperationBuilder()); return wsdl11Definition; }
内容的提问来源于stack exchange,提问作者Daniel Pomrehn
相关产品推荐
相关产品推荐

