现在有这么一个需求,我们的系统(M)作为各个系统信息交换中间平台。系统A和系统B需要交互,但是他们的网络是隔离的。系统B通过webservice发布了一些接口服务,然后提供WSDL给系统A调用,然而系统A是无法访问系统B的网络,所以数据先要发送中间系统M。其实也就是一个简单的服务转发代理的功能,只是涉及到soap协议的交互。
这里给出一种简单的方式:
系统A使用soapui导入WSDL文件,获取每个方法的请求报文->提交M->原样提交到系统B->将系统B的处理结果返回
其中主要涉及到soap协议的一些报文头的设置,下边会有调用代码说明。
1,转发的实现
主要是在中间系统M注册filter来拦截webservice请求,然后将请求数据流构造一个新的http请求发送给系统B。这里请求的发送和转换
采用httpcomponents实现。引入依赖:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(WEBSERVICE_URL);
这里构造了一个新的httpPost对象,然后读取请求流构造InputStreamEntity,接着设置httpPost的header这段。
执行CloseableHttpResponse httpResponse = httpClient.execute(httpPost);这样就获取了接口调用的结果
WEBSERVICE_URL=http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl
最后测试转发服务只要把地址栏的地址替换成filter的访问地址即可:http://localhost:8080/easyshare-web/

3,使用HttpClient进行接口调用
只要按照soapUI测试接口的头部信息发送请求报文,就可以成功调用接口。因此我们的客户端调用代码按照这个模式就可以执行接口的调用了。只是要注意我们调用的目标是转发服务的地址。
这里给出一种简单的方式:
系统A使用soapui导入WSDL文件,获取每个方法的请求报文->提交M->原样提交到系统B->将系统B的处理结果返回
其中主要涉及到soap协议的一些报文头的设置,下边会有调用代码说明。
1,转发的实现
主要是在中间系统M注册filter来拦截webservice请求,然后将请求数据流构造一个新的http请求发送给系统B。这里请求的发送和转换
采用httpcomponents实现。引入依赖:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.4</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.4</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4</version> </dependency>filter代码如下:
package cn.com.egova.easyshare.web.filter; /** * @auth gongxufan * @Date 2017/9/20 **/ import org.apache.cxf.helpers.IOUtils; import org.apache.http.Header; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.InputStreamEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Enumeration; import java.util.ResourceBundle; /** * WSDL服务器请求转发 */ public class WebServiceDispatchFilter implements Filter { private static String WEBSERVICE_URL; private static String WEBSERVICE_HOST; static { ResourceBundle bundler = ResourceBundle.getBundle("config"); // 获取调用接口的路径 WEBSERVICE_URL = bundler.getString("WEBSERVICE_URL"); WEBSERVICE_HOST = bundler.getString("WEBSERVICE_HOST"); } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; try { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(WEBSERVICE_URL); //构造新的请求数据流 InputStreamEntity entity = new InputStreamEntity( request.getInputStream()); httpPost.setEntity(entity); //复制请求参数 for (Enumeration<String> e = request.getHeaderNames(); e .hasMoreElements(); ) { String name = e.nextElement().toString(); //注意这里不要设置,发送请求会抛异常 if ("Content-Length".equalsIgnoreCase(name)) { continue; } httpPost.setHeader(name, request.getHeader(name)); } //host参数需要覆盖,设置为webservice服务提供这的主机 httpPost.setHeader("host", WEBSERVICE_HOST); //提交soap请求 CloseableHttpResponse httpResponse = httpClient.execute(httpPost); //复制soap相应的消息头 for (Header h : httpResponse.getAllHeaders()) { response.setHeader(h.getName(), h.getValue()); } //讲soap的返回流复制到filter的输出流 IOUtils.copyAndCloseInput(httpResponse.getEntity().getContent(), response.getOutputStream()); } catch (Exception e) { response.setContentType("text/xml;charset=UTF-8"); String result = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<soap:Body><soap:Fault><faultcode>" + "9999" + "</faultcode>" + "<faultstring>" + "失败:" + e.getMessage() + "</faultstring></soap:Fault></soap:Body></soap:Envelope>"; response.getWriter().print(result); } } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }代码实现也很简单:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(WEBSERVICE_URL);
这里构造了一个新的httpPost对象,然后读取请求流构造InputStreamEntity,接着设置httpPost的header这段。
执行CloseableHttpResponse httpResponse = httpClient.execute(httpPost);这样就获取了接口调用的结果
最后将httpResponse原样输出到系统A的调用者。
为了测试方便,我们使用天气预报的webservice接口进行测试,上面代码的主机和服务地址是写在配置文件的,其内容如下:
WEBSERVICE_HOST=www.webxml.com.cnWEBSERVICE_URL=http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl
配置filter拦截
<filter> <filter-name>WsTransFilter</filter-name> <filter-class>cn.com.egova.easyshare.web.filter.WebServiceDispatchFilter</filter-class> </filter> <filter-mapping> <filter-name>WsTransFilter</filter-name> <url-pattern>/webservice/*</url-pattern> </filter-mapping>2,使用soapUI进行接口测试
用浏览器访问http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl,并保存为WeatherWebService.xml。文件内容较多
这里就不展示了。打开soapUI,新建一个soap工程,并导入wsdl文件,这样就可以测试接口了。我们测试下getSupportCity这个方法。最后测试转发服务只要把地址栏的地址替换成filter的访问地址即可:http://localhost:8080/easyshare-web/

3,使用HttpClient进行接口调用
只要按照soapUI测试接口的头部信息发送请求报文,就可以成功调用接口。因此我们的客户端调用代码按照这个模式就可以执行接口的调用了。只是要注意我们调用的目标是转发服务的地址。
package cn.com.egova.easyshare.test.web; import cn.com.egova.easyshare.web.utils.HttpUtils; import java.util.ResourceBundle; /** * @auth gongxufan * @Date 2017/9/27 **/ public class SoapInvokeTest { public static void main(String[] arg) { //soap测试 String postUrl = "http://localhost:8080/easyshare-web/webservice"; //采用SOAP1.1调用服务端,这种方式能调用服务端为soap1.1和soap1.2的服务 String orderSoapXml = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://WebXml.com.cn/\">\n" + " <soapenv:Header/>\n" + " <soapenv:Body>\n" + " <web:getSupportCity>\n" + " <!--Optional:-->\n" + " <web:byProvinceName>云南</web:byProvinceName>\n" + " </web:getSupportCity>\n" + " </soapenv:Body>\n" + "</soapenv:Envelope>"; String soap1_1 = HttpUtils.doPostSoap1_1(postUrl, orderSoapXml, "http://WebXml.com.cn/getSupportCity"); System.out.println(soap1_1); //soap1.2没有soapAction头,直接设置在content-type String soap1_2 = HttpUtils.doPostSoap1_2(postUrl, orderSoapXml, "http://WebXml.com.cn/getSupportCity"); System.out.println(soap1_2); System.out.println("end"); } }
public static String doPostSoap1_1(String postUrl, String soapXml, String soapAction) { LOGGER.info("开始转发webservice:" + postUrl); String retStr = ""; // 创建HttpClientBuilder HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); // HttpClient CloseableHttpClient closeableHttpClient = httpClientBuilder.build(); HttpPost httpPost = new HttpPost(postUrl); // 设置请求和传输超时时间 /* RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(socketTimeout) .setConnectTimeout(connectTimeout).build(); httpPost.setConfig(requestConfig);*/ try { httpPost.setHeader("Content-Type", "text/xml;charset=UTF-8"); httpPost.setHeader("SOAPAction", soapAction); StringEntity data = new StringEntity(soapXml, Charset.forName("UTF-8")); httpPost.setEntity(data); CloseableHttpResponse response = closeableHttpClient .execute(httpPost); HttpEntity httpEntity = response.getEntity(); if (httpEntity != null) { // 打印响应内容 retStr = EntityUtils.toString(httpEntity, "UTF-8"); LOGGER.info("response:" + retStr); } } catch (Exception e) { LOGGER.error("exception in doPostSoap1_1", e); } finally { // 释放资源 try { closeableHttpClient.close(); } catch (IOException e) { e.printStackTrace(); } } return retStr; } public static String doPostSoap1_2(String postUrl, String soapXml, String soapAction) { String retStr = ""; // 创建HttpClientBuilder HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); // HttpClient CloseableHttpClient closeableHttpClient = httpClientBuilder.build(); HttpPost httpPost = new HttpPost(postUrl); /* // 设置请求和传输超时时间 RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(socketTimeout) .setConnectTimeout(connectTimeout).build(); httpPost.setConfig(requestConfig);*/ try { httpPost.setHeader("Content-Type", "text/xml;charset=UTF-8;action=\"" + soapAction + "\""); StringEntity data = new StringEntity(soapXml, Charset.forName("UTF-8")); httpPost.setEntity(data); CloseableHttpResponse response = closeableHttpClient .execute(httpPost); HttpEntity httpEntity = response.getEntity(); if (httpEntity != null) { // 打印响应内容 retStr = EntityUtils.toString(httpEntity, "UTF-8"); LOGGER.info("response:" + retStr); } // 释放资源 closeableHttpClient.close(); } catch (Exception e) { LOGGER.error("exception in doPostSoap1_2", e); } return retStr; }
上面1.1和1.2方法的区别就是头部设置的差异,这个是按照soapUI调用时自动生成的header来设置的。