今天做了个调WebService的功能,记录一下。
先说明下什么是WebService:
- (http + xml) = web Service
- XML:扩展性标记语言
– xmlns=“http://itcast.cn” 使用默认命名空间。
– xmlns:itcast=“http://itcast.cn”使用指定名称的命名空间。- SOAP:简单对象访问协议,用于网上传输数据。 SOAP = 在HTTP的基础上+XML数据。组成:
– Envelope – 必须的部分。以XML的根元素出现
– Headers – 可选的
– Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。- WSDL:web服务描述语言(说明书)
– 通过xml格式说明服务的地址
– 通过xml格式说明服务如何调用
我实现的WebService:
请求接口:http://iofweb.xxx.com.cn/TradeService
请求报文:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:int="http://interfaces.service.xxx.com">
<soapenv:Header/>
<soapenv:Body>
<int:getTradeData/>
</soapenv:Body>
</soapenv:Envelope>
返回报文:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<ns1:getTradeDataResponse xmlns:ns1="http://interfaces.service.xxx.com">
<ns1:out>
<fundCode xmlns="http://VO.xxx.com">000930</fundCode>
<purchaseAmount xmlns="http://VO.xxx.com">0.00</purchaseAmount>
<purchaseNum xmlns="http://VO.xxx.com">0</purchaseNum>
<redeemAmount xmlns="http://VO.xxx.com">76853.20</redeemAmount>
<redeemNum xmlns="http://VO.xxx.com">1638</redeemNum>
<retCode xmlns="http://VO.xxx.com">0000</retCode>
<retMsg xmlns="http://VO.xxx.com">success</retMsg>
<transactionDate xmlns="http://VO.xxx.com">20191022</transactionDate>
</ns1:out>
</ns1:getTradeDataResponse>
</soap:Body>
</soap:Envelope>
请求并解析返回报文:
/**
* 请求WebService,并解析返回报文
*
* @param url:接口路径 (http://iofweb.xxx.com.cn/TradeService)
* @param xpath :xml解析节点路径 (/soap:Envelope/soap:Body/ns1:getTradeDataResponse/ns1:out)
* @param packet: 请求报文(上面的)
* */
public static List<Map<String,String>> getWebServiceRecord(String url,String xpath){
ResponseEntity<String> response = null;
HttpEntity<String> request = new HttpEntity<>(packet);
try{
URI uri = URI.create(url);
response = HttpInvoker.getInstance().postForEntity(uri,request,String.class);
if (response.getStatusCode().is2xxSuccessful()) {
String responseBody = response.getBody();
if (StringUtils.contains(responseBody,ApiConst.WS_BIZ_SUCCESS_KEY)) { //请求成功的标识(自定义)
List<Map<String,String>> records = xmlToMap(xpath,responseBody);
return records;
} else {
throw Tools.newException("获取WebService数据失败:",responseBody);
}
} else {
throw Tools.newException("获取WebService数据失败:状态码[", response.getStatusCodeValue(), "]");
}
}catch (IllegalArgumentException e){
throw Tools.newException("调用WebService参数格式非法:"+Tools.getRootCauseMessage(e));
} catch (Exception e) {
throw Tools.newException("调用WebService异常:"+e);
}
}
public static List<Map<String,String>> xmlToMap(String xpath,String responseBody) throws DocumentException {
List<Map<String,String>> records = Lists.newArrayList();
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new StringReader(responseBody));
XPath xPath = document.createXPath(xpath);
Map<String, String> names = new HashMap<String, String>();
names.put("soap", "http://schemas.xmlsoap.org/soap/envelope/");
names.put(“ns1”,“http://interfaces.service.xxx.com”);
xPath.setNamespaceURIs(names);
List<Element> nodes = xPath.selectNodes(document);
if (CollectionUtils.isEmpty(nodes)) {
records = Collections.EMPTY_LIST;
} else {
for (Element node : nodes) {
Map<String,String> nodeMap = new HashMap<>();
List<Element> elements = node.elements();
elements.forEach(element -> nodeMap.put(element.getName(),element.getTextTrim()));
records.add(nodeMap);
}
}
return records;
}
这里请求用的是http请求,post方式。
调用的是接口的getTradeData方法。
用了xpath解析返回的报文:xpath的值是/soap:Envelope/soap:Body/ns1:getTradeDataResponse/ns1:out,从报文根节点开始一直到最内层。将解析后的内容以map形式保存在list中即可。
特别注意的是,在返回的报文中,ns1节点有自定义命名空间。由于WebService默认只将根节点的命名空间加载进去,程序执行时无法识别ns1的命名空间,所以需要手动添加进去,同时不能覆盖根节点soap的命名空间。即这两句代码:
names.put(“soap”, “http://schemas.xmlsoap.org/soap/envelope/”);
names.put(“ns1”,“http://interfaces.service.xxx.com”);