WebService、SOAP、WSDL的简单回顾
WebService是什么?
WebService在的概念很容易引起误解,很多初级开发人员的认知WebService接口等同于Soap接口,其实这是一种错误的认知。
WebService是相对于“本地服务”(使用同一台机器提供的服务,不需要网络)来说的一个概念。”网络服务”(Web Service)的本质,就是通过网络来调用其他服务器的资源。
常见的WebService接口类型有RPC、SOAP、Restful风格。
SOAP是什么?
SOAP全称 Simple Object Access Protocal,中文翻译为简单对象访问协议。它是一种用于交换XML编码信息的轻量级协议,用来描述传递信息的格式。一个完整的SOAP协议结构如下:
<?xml version="1.0"?>
<!--必需的 Envelope 元素,可把此 XML 文档标识为一条 SOAP 消息-->
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
<!--可选的 Header 元素,包含头部信息-->
<soap:Header></soap:Header>
<!--必需的 Body 元素,包含所有的调用和响应信息-->
<soap:Body>
<!--可选的 Fault 元素,提供有关在处理此消息所发生错误的信息-->
<soap:Fault></soap:Fault>
</soap:Body>
</soap:Envelope>
WSDL是什么?
WSDL全称Web Services Description Language,中文翻译为Web服务描述语言。它将Web服务描述定义为一组服务访问点,客户端可以通过这些服务访问点对服务进行访问。WSDL的内容也是基于XML的格式,用于描述WebServiced及其函数、参数、返回值。
一个基本的WSDL文档包含7个重要的元素。下面是一个WSDL文档的示例:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://myself.org" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://myself.org" targetNamespace="http://myself.org">
<!-- Types: 数据类型定义的容器,它使用某种类型系统(一般地使用XML Schema中的类型系统) -->
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://myself.org">
<xs:element name="XMLServiceRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="input" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="XMLServiceResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="result" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<!--Message: 通信消息的数据结构的抽象类型化定义。使用Types所定义的类型来定义整个消息的数据结构-->
<wsdl:message name="XMLServiceRequest">
<wsdl:part element="tns:XMLServiceRequest" name="XMLServiceRequest"></wsdl:part>
</wsdl:message>
<wsdl:message name="XMLServiceResponse">
<wsdl:part element="tns:XMLServiceResponse" name="XMLServiceResponse"></wsdl:part>
</wsdl:message>
<!--PortType: 对于某个访问入口点类型所支持的操作的抽象集合,这些操作可以由一个或多个服务访问点来支持-->
<wsdl:portType name="DemoServiceSoap">
<!--Operation: 对服务中所支持的操作的抽象描述,一般单个Operation描述了一个访问入口的请求/响应消息对-->
<wsdl:operation name="XMLService">
<wsdl:input message="tns:XMLServiceRequest" name="XMLServiceRequest"></wsdl:input>
<wsdl:output message="tns:XMLServiceResponse" name="XMLServiceResponse"></wsdl:output>
</wsdl:operation>
</wsdl:portType>
<!--Binding: 特定端口类型的具体协议和数据格式规范的绑定-->
<wsdl:binding name="DemoServiceSoapSoap11" type="tns:DemoServiceSoap">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="XMLService">
<soap:operation soapAction="http://myself.org/DemoService.XMLService"/>
<wsdl:input name="XMLServiceRequest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="XMLServiceResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<!--Service: 相关服务访问点的集合-->
<wsdl:service name="DemoServiceSoap">
<!--Port: 定义为协议/数据格式绑定与具体Web访问地址组合的单个服务访问点-->
<wsdl:port binding="tns:DemoServiceSoapSoapSoap11" name="DemoServiceSoapSoapSoap11">
<soap:address location="http://127.0.0.1:8080/ws/DemoService.XMLService.CLS"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
基于SpringBoot快速发布SOAP接口
本文所写示例基于springboot 2.0.4.RELEASE版本。
添加依赖
<!-- tag::springws[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
<!-- end::springws[] -->
定义数据结构,创建src/main/resources/countries.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://spring.io/guides/gs-producing-web-service"
targetNamespace="http://spring.io/guides/gs-producing-web-service" elementFormDefault="qualified">
<xs:element name="getCountryRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getCountryResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="country" type="tns:country"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="country">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="population" type="xs:int"/>
<xs:element name="capital" type="xs:string"/>
<xs:element name="currency" type="tns:currency"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="currency">
<xs:restriction base="xs:string">
<xs:enumeration value="GBP"/>
<xs:enumeration value="EUR"/>
<xs:enumeration value="PLN"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
根据Schma生成实体对象
在pom.xml
中添加如下插件,执行mvn compile
即可在src/main/java
目录下发现生成的实体对象类。
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
创建数据仓库访问类
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import io.spring.guides.gs_producing_web_service.Country;
import io.spring.guides.gs_producing_web_service.Currency;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
@Component
public class CountryRepository {
private static final Map<String, Country> countries = new HashMap<>();
@PostConstruct
public void initData() {
Country spain = new Country();
spain.setName("Spain");
spain.setCapital("Madrid");
spain.setCurrency(Currency.EUR);
spain.setPopulation(46704314);
countries.put(spain.getName(), spain);
Country poland = new Country();
poland.setName("Poland");
poland.setCapital("Warsaw");
poland.setCurrency(Currency.PLN);
poland.setPopulation(38186860);
countries.put(poland.getName(), poland);
Country uk = new Country();
uk.setName("United Kingdom");
uk.setCapital("London");
uk.setCurrency(Currency.GBP);
uk.setPopulation(63705000);
countries.put(uk.getName(), uk);
}
public Country findCountry(String name) {
Assert.notNull(name, "The country's name must not be null");
return countries.get(name);
}
}
创建服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import io.spring.guides.gs_producing_web_service.GetCountryRequest;
import io.spring.guides.gs_producing_web_service.GetCountryResponse;
@Endpoint //使用Spring WS注册该类作为处理传入SOAP消息的潜在候选者
public class CountryEndpoint {
private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service";
private CountryRepository countryRepository;
@Autowired
public CountryEndpoint(CountryRepository countryRepository) {
this.countryRepository = countryRepository;
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest") //Spring WS使用它来根据消息的名称空间和localPart选择处理程序方法。
@ResponsePayload //将返回值映射到响应
public GetCountryResponse getCountry(
@RequestPayload GetCountryRequest request // 表示传入消息将映射到方法的请求参数
) {
GetCountryResponse response = new GetCountryResponse();
response.setCountry(countryRepository.findCountry(request.getName()));
return response;
}
}
配置WebService服务
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
@EnableWs // 开启WebService
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*"); // 通过"/ws/*"路径来发布WSDL文件
}
@Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
// 使用XsdSchema公开标准WSDL 1.1 发布描述文件,访问路径:http://<host>:<port>/ws/countries.wsdl
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CountriesPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-producing-web-service");
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
}
}
创建启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
测试
查看WSDL文件
在浏览器中访问:http://<host>:<port>/ws/countries.wsdl
,将会看到WSDL文件。
发送SOAP测试请求
本地创建request.xml
,内容如下:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="http://spring.io/guides/gs-producing-web-service">
<soapenv:Header/>
<soapenv:Body>
<gs:getCountryRequest>
<gs:name>Spain</gs:name>
</gs:getCountryRequest>
</soapenv:Body>
</soapenv:Envelope>
使用curl
在、在终端中执行如下指令:
$ curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws
测试成功的结果如下:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service">
<ns2:country>
<ns2:name>Spain</ns2:name>
<ns2:population>46704314</ns2:population>
<ns2:capital>Madrid</ns2:capital>
<ns2:currency>EUR</ns2:currency>
</ns2:country>
</ns2:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>