1.简单描述
WebService简单理解就是用http发送接收xml数据,但这个xml得遵守系统的规范。这个规范就是WSDL(Web服务描述语言,Web Services Description Language)。
在WebService中传输的xml有一个正式的名称叫Soap(简单对象访问协议 Simple Object Access Protocol)。
WebService分为客户端和服务端。这两个都可以做数据源提供数据,比如说客户端发送大量数据给服务端,服务端接收大量数据。也可以是客户端发起请求获取服务端提供的大量数据。所有谁生产谁消费这事对Webservice不必纠结。
2.代码实现
SpringBoot项目中还是推荐使用注解的方式实现WebService,这样比较优雅。
1.首先要引入大量的依赖。目前部分jar可能有漏斗,如果项目对安全有较高的要求请引入没有漏洞版本的jar
<!-- webService依赖-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>${cxf-core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf-core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf-core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf-core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>${cxf-core.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-ri</artifactId>
<version>2.3.5</version>
<type>pom</type>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.cxf.services/cxf-services -->
<dependency>
<groupId>org.apache.cxf.services</groupId>
<artifactId>cxf-services</artifactId>
<version>3.4.3</version>
<type>pom</type>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-bindings-soap -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-bindings-soap</artifactId>
<version>${cxf-core.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-api -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-api</artifactId>
<version>2.7.18</version>
</dependency>
接着实现服务器端的代码
服务器端的代码有两部分一部分是WebService接口,一部分是发布WebService的配置类
接口定义(规范WSDL)
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* Webservice 发送数据
* @author dhh
*/
@WebService(serviceName = "WebServiceSender",
targetNamespace="http://service.modules.jeecg.org/",
endpointInterface = "org.jeecg.modules.service.WebServiceServer")
public interface WebServiceServer {
@WebMethod(operationName = "Invoke")
void invoke(@WebParam(name="from",targetNamespace = "http://service.modules.jeecg.org/")String from,
@WebParam(name="token",targetNamespace = "http://service.modules.jeecg.org/")String token,
@WebParam(name="funcName",targetNamespace = "http://service.modules.jeecg.org/")String funcName,
@WebParam(name="parameters",targetNamespace = "http://service.modules.jeecg.org/")String parameters) throws Exception;
}
实现类(接收到请求后服务器端做处理)
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.service.WebServiceServer;
import org.springframework.stereotype.Service;
/**
* @author dhh
*/
@Slf4j
@Service
public class WebServiceSenderServiceImpl implements WebServiceServer {
@Override
public void invoke(String from, String token, String funcName, String parameters) throws Exception {
if("数据来源".equals(from)){
switch (funcName) {
}
}else{
log.info("未知来源的数据{},禁止写入!",from);
throw new Exception("未知来源的数据,禁止写入");
}
}
}
配置类(发布服务)
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.jeecg.modules.service.WebServiceServer;
import org.jeecg.modules.service.impl.WebServiceSenderServiceImpl;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
/**
* @author dhh
*/
@Configuration
public class WebServiceConfig {
@Bean(name = "cxfServlet") // 注入servlet bean name不能dispatchlet ,否则会覆盖dispatcherServlet
public ServletRegistrationBean<CXFServlet> cxfServlet() {
return new ServletRegistrationBean<CXFServlet>(new CXFServlet(), "/webservice/*");
}
@Bean
public WebServiceServer webServiceSender() {
return new WebServiceSenderServiceImpl();
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Bean
public Endpoint endpoint() {
// 参数二,是SEI实现类对象
EndpointImpl endpoint = new EndpointImpl(this.springBus(),this.webServiceSender());
// 发布服务
endpoint.publish("/server");
return endpoint;
}
}
接下来启动项目访问/webservice/server?wsdl 就可以看看WSDL的内容了。
然后搭建客户端
客户端搭建很简单,就是用http发送一个xml,困难在xml数据的格式要遵守服务端定义的wsdl要求。比如命名空间要和服务端的一致(http://service.modules.jeecg.org/),参数数量和名称也要和服务端定义的一致方可
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jeecg.modules.bean.WebServiceRequest;
import org.jeecg.modules.entity.AttachmentInfo;
import org.jeecg.modules.entity.ProjectInfo;
import org.jeecg.modules.entity.ProjectScope;
import org.jeecg.modules.service.WebServiceClientService;
import org.springframework.stereotype.Service;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
* @author dhh
*/
@Slf4j
@Service
public class WebServiceClientServiceImpl implements WebServiceClientService {
/**
* 发送请求,发送请求时,将数据封装起来
*/
@Override
public String send(WebServiceRequest request, String xmlEntry) {
try {
URL url = new URL(request.getRequestUrl());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
connection.setDoOutput(true);
connection.setDoInput(true);
String xml = this.requestXmlBuilder(request.getEsbCode(), request.getComp(),request.getFrom(),request.getToken(), request.getFuncName(), xmlEntry);
connection.setRequestProperty("Content-Length",String.valueOf(xml.length()));
OutputStream out = connection.getOutputStream();
out.write(xml.getBytes(StandardCharsets.UTF_8));
int responseCode = connection.getResponseCode();
if(responseCode==200){
InputStream in = connection.getInputStream();
String result = getResult(in);
log.info(result);
return result;
}
} catch (IOException e) {
log.error(e.getMessage());
}
return "ERROR";
}
这里我将构建xml单独写了一个方法,可以根据服务端WSDL规范自行定义
private String requestXmlBuilder(String esbCode,String COMP,String from ,String token,String funcName,String xml){
StringBuffer buffer= new StringBuffer();
buffer.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:boi=\"http://service.modules.jeecg.org/\">");
buffer.append("<soapenv:Header>");
buffer.append("<extraParams>");
buffer.append("<esbCode>");
// esbCode:数据
buffer.append(esbCode);
buffer.append("</esbCode>");
buffer.append("<COMP>");
// COMP:数据
buffer.append(COMP);
buffer.append("</COMP>");
buffer.append("</extraParams>");
buffer.append("</soapenv:Header>");
buffer.append("<soapenv:Body>");
buffer.append("<boi:Invoke>");
buffer.append("<boi:from>");
buffer.append(from);
buffer.append("</boi:from>");
buffer.append("<boi:token>");
buffer.append(token);
buffer.append("</boi:token>");
buffer.append("<boi:funcName>");
// funcName:数据
buffer.append(funcName);
buffer.append("</boi:funcName>");
buffer.append("<boi:parameters><![CDATA[");
// XML:数据
buffer.append(xml);
buffer.append("]]></boi:parameters>");
buffer.append("</boi:Invoke>");
buffer.append("</soapenv:Body>");
buffer.append("</soapenv:Envelope>");
return buffer.toString();
}
后面直接调用客户端方法请求本地服务端就可以测试客户端了,以上结束。
参考连接:webservice关于入参掉用各种报错信息及解决方法汇总org.apache.cxf.interceptor.Fault: Unmarshalling Error: 意外的元素...... - 走看看
实例篇——webservice实现互相传递数据 - 走看看
Apache CXF -- Developing a Service
后面还有拦截器的用法,感兴趣的可以了解一下!
版权归原作者 会会会会 所有, 如有侵权,请联系我们删除。