java使用webservice的讲解
做项目的时候,经常遇到接口是webservice提供的,这个时候就需要会进行调试;
调试可以用soapui、postman/apipost等同类工具。
理解:webservice本身可以理解为一种xml格式,具有自描述特点的http post方式请求。可以完全适用http方式去调用。所以不要有任何压力。
webservice的几种解析调用方式
方式 | 优势 | 缺点 | 上手难度 | 链接 |
---|---|---|---|---|
SoupUi | 只要获取到wsdl 的xml文件的就可以一键解析出所有的方法;非常简单;软件支持windows、mac、linux | 需要多安装一个软件而已 | 最简单,需要安装soapui | soupui.org download |
Postman | 软件简单常见 | 需要手工解析wsdl中的方法,请求参数、方式、手工粘贴到postman中,比较繁琐。首个接口难道大,webservice本质上就是一种特殊的Http请求,使用Http相关的工具完全适用 | 第一个方法难度大 | postman、apipost |
wsimport | java jdk自带 | 需要懂jdk命令,不直观,有一定难度。 | 最大 | 使用JAVA命令wsimport生成WebService接口调用代码](https://blog.youkuaiyun.com/mc_linfen/article/details/83275184) |
一、 webservice介绍
1.1.1 wsdl
wsdl:webservice中的核心概念,所有方法定义都在wsdl中,全程 webservice define language;
soap协议:soap协议是基于http协议,限制使用数据内容为xml,请求方式必须是Http Post的一个http协议的请求。具有:soap1.1、soap1.2两个版本。
1.1.2 webxml.com.cn公开的wsdl
上面几个几个wsdl的示例,这里介绍几个公开的wsdl链接示例,见公开网站 webxml.com.cn
wsdl | 作用 | endpoint |
---|---|---|
MobileCodeWS | 国内手机号归属地 | 归属地wsdl网站 |
WeatherWS | 城市天气 | 2500多个城市天气预报 WEB服务 |
ChildService.xml | xx免疫规划系统 | |
baseInfoWebService.xml | xx冷链系统 | |
externalWebService.xml | xx冷链系统 | |
medicalWebService.xml | xx冷链系统 | |
为防止,wsdl连接失效,我们copy一个wsdl定义: |
1.1.3、wsdl示例1-baseInfoWebService.xml
baseInfoWebService.xml
原始地址(不可访问):https://yxxx.xa.gov.cn/ws/services/baseInfoWebService?wsdl、
可访问地址:baseInfoWebService
二 工具使用
2.1 soupui 使用
soupui有免费开源版本,跨平台;
2.2 postman或者同类工具apipost使用
2.3 java自带命令行 wsimport
2.4 使用postman或者apipots等同类工具生成代码
2.4.1、getCountryCityByIp-SOAP1.1
curl --request POST \
--url http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx \
--header 'Content-Type: text/xml; charset=utf-8' \
--header 'content-type: application/xml' \
--data '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getCountryCityByIp xmlns="http://WebXml.com.cn/">
<theIpAddress>222.91.66.232</theIpAddress>
</getCountryCityByIp>
</soap:Body>
</soap:Envelope>'
2.4.2、getCountryCityByIp-SOAP1.2
curl --request POST \
--url http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx \
--header 'Content-Type: application/soap+xml; charset=utf-8' \
--header 'content-type: application/xml' \
--data '<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<getCountryCityByIp xmlns="http://WebXml.com.cn/">
<theIpAddress>222.91.66.232</theIpAddress>
</getCountryCityByIp>
</soap12:Body>
</soap12:Envelope>'
三、名词解释
名词 | 作用 | 链接 |
---|---|---|
soap协议 | xml格式的post方式的http请求 | |
wsdl定义 | 就是soup协议的xml;协议版本有1.1和1.2两个版本l | |
cxf | 解析、发布soup xml的一个java的jar包 | apache cxf |
hutool | 更加方便请求http请求 | Soap客户端-SoapClient |
四、java一键解析xml成bean
4.1 json一键解析成java bean
4.2 xml 一键解析成实体类
4.2.1 xml先转成json
使用在线转换工具,直接转换xml为json;
4.2.2 xml使用idea转成xml
4.2.3 实体转xml工具类
1、JAXB "有两个名为 “" 的属性,类的两个属性具有相同名称 "”"解决方案
2、java实体 和 xml相互转换
/**
1. 4.3 儿童信息上传接口 uploadChildrenInfo
2. 日期统一 1991-10-08 00:00:00 这种格式
3. 字符串长度的一半表示纯汉字长度
*/
@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "children")
public class JwxVaccineDetailVo {
@XmlElement(name = "child")
private List<Child> children;
@XmlElement(name = "bact_code", required = true)
protected String bactCode;
@XmlElementWrapper(name="inoculations")//指定父节点, = 等于多包裹了一层;
@XmlElement(name = "inoculation")
private List<Inoculation> inoculations;
}
4.3 idea根据xml直接生成实体类
webservice生成实体类是重复工作,使用idea自动生成
- 获取xml样例
- 使用idea根据xml生成xsd定义文件
- 使用idea根据xsd生成实体类(使用jaxb架构从xml生成java实体)
五、研发webService的发布
webService开发完毕需要进行发布,发布需要借助:org.apache.cxf.jaxws.EndpointImpl.publish() 方法。
5. 1 需要引入的包
序号 | maven坐标 | 常见类 | 核心作用 |
---|---|---|---|
1 | jakarta.jws-api | javax.jws.@WebService、@WebParam、@WebMethod | 标记该service是是webserice |
2 | org.apache.cxf | org.apache.cxf.jaxws.EndpointImpl | 对ws进行发布publish |
5.2 使用示例
5.2.1 接口PacsService
/**
* Pacs服务
* @author li_chengyu
*/
@WebService(name = "PacsService", // 暴露服务名称
targetNamespace = "http://service.listener.neusoft.com"// 命名空间,一般是接口的包名倒序
)
public interface PacsService {
/**
* (病理检查)报告结果信息接收服务
*
* @param xml
* @return
* @throws Exception
*/
@WebMethod
String receivePacsPathologicalRecord(@WebParam(name = "xml") String xml) throws Exception;
}
5.2.2 实现PacsServiceImpl
@Service
@WebService(name = "PacsService",
serviceName = "PacsService",
targetNamespace = "http://service.listener.neusoft.com",
endpointInterface = "com.neusoft.listener.service.business.terminal.PacsService"
)
@Component
@MapperScan({"com.neusoft.base.dal.dao.*"})
public class PacsServiceImpl implements PacsService {
@Override
public String receivePacsPathologicalRecord(String content) throws Exception {
SoapReturnDto returnDto = new SoapReturnDto();
return XmlSerializer.bean2Xml(returnDto);
}
}
5.2.3 发布public ws
SpringBoot中使用WebService(简单的使用)
import com.neusoft.listener.service.business.terminal.PacsService;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
@Configuration
public class CxfConfig {
@Autowired
private PacsService pacsService;
/**
* 检验
*
* @author Tony
* @date
*/
@Bean
public Endpoint normalService() {
EndpointImpl endpoint = new EndpointImpl(bus, normalService);
endpoint.publish("/NormalService");
return endpoint;
}
/**
* 检查
*
* @author Tony
* @date
*/
@Bean
public Endpoint pacsService() {
EndpointImpl endpoint = new EndpointImpl(bus, pacsService);
endpoint.publish("/PacsService");
return endpoint;
}
}
六、xml读取和webservice请求
webService开发完毕需要进行发布,发布需要借助:org.apache.cxf.jaxws.EndpointImpl.publish() 方法。
6.1 发起webservice请求
/**
* @author dazer
*/
@Getter
public enum JwxWsShengjiMethodEnum {
/**
* 接口文档来源:
* xpath使用,见 hutool https://www.hutool.cn/docs/#/core/工具类/XML工具-XmlUtil?id=xml%e4%b8%8e%e5%af%b9%e8%b1%a1%e8%bd%ac%e6%8d%a2
*/
UPLOAD_CHILDREN_INFO("2、儿童信息上传接口", "web:uploadChildrenInfo", "//soapenv:Envelope/soapenv:Body/ns1:uploadChildrenInfoResponse/uploadChildrenInfoReturn"),
DOWNLOAD_VACC_BY_CHILD_NO("3、异地接种查询接口之一(任何接种点都可以根据【childNo=?chilCode 档案编码】下载接种信息)","web:downloadVaccByChildNo", "//soapenv:Envelope/soapenv:Body/ns1:downloadVaccByChildNoResponse/downloadVaccByChildNoReturn"),
DOWNLOAD_VACC_BY_BIRTH_NO("4、异地接种查询接口之二【暂时没有找到出生证 T610289178】(任何接种点都可以根据【birthNo 儿童出生证号】下载接种信息)","web:downloadVaccByBirthNo", "//soapenv:Envelope/soapenv:Body/ns1:downloadVaccByBirthNoResponse/downloadVaccByBirthNoReturn"),
DOWNLOAD_VACC_BY_CHILD_ID("5、异地接种查询接口之三(任何接种点都可以根据【childId身份证,身份证可能下载不到,如:18个8】下载接种信息)","web:downloadVaccByChildID", "//soapenv:Envelope/soapenv:Body/ns1:downloadVaccByChildIDResponse/downloadVaccByChildIDReturn"),
DOWNLOAD_VACC_BY_NAME("6、异地接种查询接口之四(根据建档地址、孩子姓名、母亲姓名查询儿童信息)","web:downloadVaccByName", "//soapenv:Envelope/soapenv:Body/ns1:downloadVaccByNameResponse/downloadVaccByNameReturn"),
DOWNLOAD_MIGRATION_CHILD_NO("7、已在异地接种儿童编码下载接口(返回本接种点的儿童在异地接种的记录)","web:downloadMigrationChildNo", "//soapenv:Envelope/soapenv:Body/ns1:downloadMigrationChildNoResponse/downloadMigrationChildNoReturn"),
;
/**
* webservice请求方法
* @param method endPoint 端点, 如:http://10.5.97.108:10001/ws/services/baseInfoWebService.baseInfoWebServiceHttpSoap12Endpoint/
* @param method 方法名称,如:ws:getBatchs
* @param xPath 获取有效数据的path, 如://soapenv:Envelope/soapenv:Body/ns:getBatchsResponse/ns:return
*/
JwxWsShengjiMethodEnum(String methodName, String method, String xPath) {
this.methodName = methodName;
this.method = method;
this.xPath = xPath;
}
private final String methodName;
private final String method;
private final String xPath;
@Override
public String toString() {
return this.getMethodName();
}
}
public static void main(String[] args) {
Map<String, Object> paramsMap = new HashMap();
String methodName = methodEnum.getMethodName();
// webservice请求方法,如:ws:getBat
String method = methodEnum.getMethod();
// 返回xml,获取我们需要数据的路径,如:"//soapenv:Envelope/soapenv:Body/ns:getBatchsResponse/ns:return";
String xPath = methodEnum.getXPath();
// 处理params
// 新建客户端
/**
* 完整地址:https://yqpt.xa.gov.cn/ws/services/baseInfoWebService.baseInfoWebServiceHttpSoap11Endpoint/
*/
int timeoutSecond = vficProperties.getArchives().getTimeout();
String nameSpaceUrl = "http://webservices.web.vaccine.nipm.jwx.com";
// 必须添加soap action
SoapClient client = SoapClient.create(endPoint, SoapProtocol.SOAP_1_2).
// 设置要请求的方法,此接口方法前缀为web,传入对应的命名空间
setMethod(method, nameSpaceUrl).
// 设置参数,此处自动添加方法的前缀:web
setParams(paramsMap, false).
timeout(timeoutSecond * 1000);
Map<String, String> header = new HashMap<>(1);
header.put("SOAPAction", endPoint);
client.addHeaders(header);
// 发送请求,参数true表示返回一个格式化后的XML内容
// 返回内容为XML字符串,可以配合XmlUtil解析这个响应
String xmlStr="";
Document docResult = null;
try {
docResult = XmlUtil.readXML(xmlStr);
} catch (UtilException | IllegalArgumentException e) {
//xml 出错
log.error("xml parse 错误");
}
// xpath使用,见 hutool https://www.hutool.cn/docs/#/core/工具类/XML工具-XmlUtil?id=xml%e4%b8%8e%e5%af%b9%e8%b1%a1%e8%bd%ac%e6%8d%a2
String value = XmlUtil.getByXPath(xPath, docResult, XPathConstants.STRING) + "";
log.debug("xml getByXPath获取数据 " + value);
}
6.2 根据xml xpath获取取值 (XmlUtil.getByXPath)
// 发送请求,参数true表示返回一个格式化后的XML内容
// 返回内容为XML字符串,可以配合XmlUtil解析这个响应
String xmlStr="";
Document docResult = null;
try {
docResult = XmlUtil.readXML(xmlStr);
} catch (UtilException | IllegalArgumentException e) {
//xml 出错
log.error("xml parse 错误");
}
// xpath使用,见 hutool https://www.hutool.cn/docs/#/core/工具类/XML工具-XmlUtil?id=xml%e4%b8%8e%e5%af%b9%e8%b1%a1%e8%bd%ac%e6%8d%a2
String value = XmlUtil.getByXPath(xPath, docResult, XPathConstants.STRING) + "";
log.debug("xml getByXPath获取数据 " + value);
6.3 解析XMl报错
6.3.1、Nested exception: 对实体 “XXX” 的引用必须以 ‘;’ 分隔符结尾
分析:xml value中包含,特殊符号,如:< 小于号、> 大于号、& 和、’ 单引号、" 双引号
参考:记异常:对实体 ”XXX“ 的引用必须以 ‘;’ 分隔符结尾
问题:xml搜索不到,就需要从:originalText base64解密出来查看
6.3.2、The parse error. rowNo[1]xPath[]to[]msg[Error on line 1 of document : 元素内容必须由格式正确的字符数据或标记组成。 Nested exception: 元素内容必须由格式正确的字符数据或标记组成。]
问题分析:xml正文内容包含: < 小于号、> 大于号、& 和、’ 单引号、" 双引号
EncodeDecode
xml在线格式化、校验
在线XML验证