WebService简介
WebService接口的发布通常一般都是使用WSDL(web service descriptive language)文件的样式来发布的,该文档包含了请求的参数信息,返回的结果信息,我们需要根据WSDL文档的信息来编写相关的代码进行调用WebService接口。接下来我将采用常见的两种方式调用WebService接口。
场景描述
目前我需要使用java调用C#系统的一个WebService接口,传递参数为一个表号,返回的是一个Xml的数据类型,需要实现调用接口,获取到xml之后并解析为Json格式数据,并返回给前端。Java调用WebService接口,需要根据提供接口方的XSD文档编写相关代码,Xsd文档可以直接通过提供的接口地址进行查看。
WebServiceTemplate调用WebService接口实现
1.导入相关的依赖包,如下:
<dependency>
<groupId></groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
2.使用WebServiceTemplate实现调用WebService接口,需要编写先关的解析类,根据提供的XSDL文档编写先关代码,XSDL文档信息如下:
POST /rootServiceFlow/ HTTP/1.1
Host: 10.200.0.74
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "/AAFlow002x"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:xsd="http:///2001/XMLSchema" xmlns:soap="/soap/envelope/">
<soap:Body>
<AAFlow002x xmlns="/">
<LotNo>string</LotNo>
</AAFlow002x>
</soap:Body>
</soap:Envelope>
上面的信息是包含请求所需要传递的参数,字段为LotNo。接下来XSDL的文档信息为返回的数据格式,如下:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:xsd="http:///2001/XMLSchema" xmlns:soap="/soap/envelope/">
<soap:Body>
<AAFlow002xResponse xmlns="/">
<AAFlow002xResult>string</AAFlow002xResult>
</AAFlow002xResponse>
</soap:Body>
</soap:Envelope>
需要根据以上信息编写请求类AAFlow002x,响应接收解析类AAFlow002xResponse,和ObjectFactory类,这三个类可以使用Idea的终端通过命令的方式生成对应的java类,也可以通过自己编写生成java类。通过命令的方式如下:
xjc -d /path/to/output /
/path/to/output为生成类的目录,/为webServce的接口地址,生成的类如下:
AAFlow002x类编写如下:
import ;
import .*;
@XmlAccessorType()
@XmlType(name = "", propOrder = {
"LotNo"
})
@XmlRootElement(name = "AAFlow002x", namespace = "/")
@Data
public class AAFlow002x {
@XmlElement(name = "LotNo", namespace = "/", required = true)
protected String LotNo;
}
AAFlow002xResponse类编写如下:
import ;
import .*;
@XmlAccessorType()
@XmlType(name = "AAFlow002xResponse", namespace = "/", propOrder = {
"AAFlow002xResult"
})
@XmlRootElement(name = "AAFlow002xResponse")
@Data
public class AAFlow002xResponse {
@XmlElement(name = "AAFlow002xResult", namespace = "/", required = true)
private String AAFlow002xResult;
}
ObjectFatoty类编写如下:
import ;
import ;
import ;
import ;
@XmlRegistry
public class ObjectFactory {
private final static QName _AAFlow002_QNAME = new QName("/", "AAFlow002x");
private final static QName _AAFlow002Response_QNAME = new QName("/", "AAFlow002xResponse");
public ObjectFactory() {
}
public AAFlow002x createAAFlow002x() {
return new AAFlow002x();
}
public AAFlow002xResponse createAAFlow002xResponse() {
return new AAFlow002xResponse();
}
@XmlElementDecl(namespace = "/", name = "AAFlow002x")
public JAXBElement<AAFlow002x> createAAFlow002x(AAFlow002x value) {
return new JAXBElement<AAFlow002x>(_AAFlow002_QNAME, , null, value);
}
@XmlElementDecl(namespace = "/", name = "AAFlow002yResponse")
public JAXBElement<AAFlow002xResponse> createAAFlow002yResponse(AAFlow002xResponse value) {
return new JAXBElement<AAFlow002xResponse>(_AAFlow002Response_QNAME, , null, value);
}
}
编写使用WebServiceTemplate进行调用
@Service
@Slf4j
public class TestWebService {
private static final String ENDPOINT_URL = "这里填写你调用的WebService接口地址";
private static final String SOAP_ACTION = "/AAFlow002x";
public String getDataTable(String LotNo) {
// 创建WebServiceTemplate对象
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// 设置解析类,这里填写包的路径为AAFlow002xResponse类所在路径
("");
(marshaller);
(marshaller);
// 封装请求参数
AAFlow002x aaFlow002 = new ObjectFactory().createAAFlow002x();
("22113102");
// 创建SOAP请求回调对象,这里的SOAP_ACTION指定你调用的接口的哪个方法
SoapActionCallback soapActionCallback = new SoapActionCallback(SOAP_ACTION);
// 调用WebService接口,发送请求并返回数据
JAXBElement<AAFlow002xResponse> aaFlow002xResponse = (JAXBElement<AAFlow002xResponse>) (ENDPOINT_URL, aaFlow002, soapActionCallback);
String result = ().getAAFlow002xResult();
// 输出响应结果
(().getAAFlow002xResult());
return result;
}
}
编写完成之后,Debug启动项目。出现如下图所示证明调用接口成功:
我们可以针对以上代码进行优化,写一个WebServiceConfig类,专门对WebServiceTemplate进行配置,这里就不在赘述。
采用WebServiceTemplate接口调用WebService接口,虽然可以采用命令的方式生成对应的Java代码,但是其缺点是如果请求的参数和返回的参数数据结构很复杂,生成的java类代码就很复杂。
HttpClientBuilder调用WebService接口实现
@Override
public JSONObject getDataFromMESSystem(String deviceNumber) {
// 根据上面的XSDL文档封装请求参数
String strParameter = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<soap:Envelope xmlns:xsi=\"http:///2001/XMLSchema-instance\" xmlns:xsd=\"http:///2001/XMLSchema\" xmlns:soap=\"/soap/envelope/\">\n" +
" <soap:Body>\n" +
" <AAFlow002y xmlns=\"/\">\n" +
" <LotNo>" + deviceNumber + "</LotNo>\n" +
" </AAFlow002y>\n" +
" </soap:Body>\n" +
" </soap:Envelope>";
// 获取数据,返回的是一个xml格式数据
String xmlData = doPostSoap(UrlConstant.MES_SERVICE_URL, strParameter, UrlConstant.MES_SOAP_URI);
JSONObject jsonObject = null;
try {
// 将请求结果转换成json类型
if((xmlData)){
jsonObject = xml2Json(xmlData);
}
} catch (Exception e) {
();
}
return jsonObject;
}
/**
* 发送Soap请求,并返回数据
* @param url WebService接口地址
* @param soap 封装的请求参数
* @param SOAPAction 对应的调用方法uri
* @return 返回xml数据,用一个字符串接收
*/
public static String doPostSoap(String url, String soap, String SOAPAction) {
// 请求体
String retStr = "";
// 创建HttpClientBuilder
HttpClientBuilder httpClientBuilder = ();
// HttpClient
CloseableHttpClient closeableHttpClient = ();
HttpPost httpPost = new HttpPost(url);
try {
("Content-Type", "text/xml;charset=UTF-8");
("SOAPAction", SOAPAction);
StringEntity data = new StringEntity(soap,
("UTF-8"));
(data);
CloseableHttpResponse response = closeableHttpClient
.execute(httpPost);
HttpEntity httpEntity = ();
if (httpEntity != null) {
// 打印响应内容
retStr = (httpEntity, "UTF-8");
("response:" + retStr);
}
// 释放资源
();
} catch (Exception e) {
();
}
return retStr;
}
接下来需要根据返回的xml格式数据解析为Json格式,我们可以用Postman测试WebService接口,查看数据返回格式,如下图所示:
点击Send,返回的数据格式如下图所示:
数据太多,这里我给出返回的缩减的结构数据,如下
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="/soap/envelope/" xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:xsd="http:///2001/XMLSchema">
<soap:Body>
<AAFlow002yResponse xmlns="/">
<AAFlow002yResult>
<xs:schema xmlns="" xmlns:xs="http:///2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="AAFlow002" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="AAFlow002">
<xs:complexType>
<xs:sequence>
<xs:element name="F1000" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<DocumentElement xmlns="">
<AAFlow002 diffgr: msdata:rowOrder="0">
<F1000>2596</F1000>
</AAFlow002>
</DocumentElement>
</diffgr:diffgram>
</AAFlow002yResult>
</AAFlow002yResponse>
</soap:Body>
</soap:Envelope>
根据上面的信息,编写解析xml数据转换为Json格式数据代码如下:
/**
* 解析webservice的返回结果,将xml解析为json格式数据
* @param xmlStr xml内容
* @return
*/
public static JSONObject xml2Json(String xmlStr) throws DocumentException {
Document doc = (xmlStr);
Element root = ();
Element body = ("Body");
Element response = ("AAFlow002yResponse");
Element result = ("AAFlow002yResult");
Element diffgram = (new QName("diffgram", ("urn:schemas-microsoft-com:xml-diffgram-v1")));
Element documentElement = ("DocumentElement");
Element aaFlow002 = ("AAFlow002");
JSONObject jsonObject = new JSONObject();
Iterator<Element> iterator = ();
while (()) {
Element element = ();
((), ());
}
return jsonObject;
}
通过HttpClient的方式调用WebService接口,缺点是需要自己编写解析xml的代码,而WebServiceTemplate的方式可以自动解析为对应的Java类,但是个人更偏向于使用HttpClient的方式调用WebService接口,WebServiceTemplate方式,代码复杂度比较高,耦合性太强。