一、前言
最近在搞一个接口数据对接的项目,是采用Webservice进行数据对接。因之前一直采用视图、JSON或proto的方式对接,但是对webservice不太了解,所以通过这个项目把自己对webservice的理解总结记录下来。
二、基本概念
1. WebService
webservice就是字面意思:web + service,也就是 服务(service)网络(web)化的意思。他力求的是跨语言,跨平台的,基于web传输的远程调用能力。他没有强调远程调用使用什么协议,所以你可以自由选择,比如soap 协议(可与基于http,smtp,等各种传输协议),或者常见的基于http的json化的数据传输协议,基于dubbo协议的dubbo服务调用都属于web service的一种实现。【XML Web services 概述】
2. SOAP
soap是简单对象访问协议的缩写,是一种可以基于HTTP协议的访问方式。客户端发送请求,然后调用带参数的服务端函数得到服务端的数据;服务端编写处理函数并响应客户端。
3. WSDL
wsdl是网络服务者动态语言的缩写,这里面定义了双方通信时包含的东西。客户端把wsdl文件给服务端,服务端分析wsdl并写出里面的函数,这样两者无论是什么平台、什么语言都可以通信。
理解wsdl的网站:【Web Service描述语言 WSDL 详解】【描述 Web 服务:WSDL】
运行实例网站:【在PHP中利用wsdl创建标准webservice】
借鉴网站:【PHP使用WSDL格式Soap通信】
一个 WSDL 协议实例描述五项内容:
1. types,Web 服务接口的数据类型(其参数和返回类型)。
2. message,将数据类型变量分组以进行网络传输。
3. portType,将消息分组成逻辑操作。
4. binding,描述如何将 portType 映射成传输/消息传递协议。
5. service,列出某个特定绑定的连接信息。
以下为个人阅读WSDL文档的总结:
1. types的子元素complexType元素的name属性值为数组变量名, 子元素element的name属性值为数组变量名的键名;
2. message元素的name属性值为函数方法名,子元素part的type属性值为方法的形参变量名(变量名的定义在types中),name数据值为任意值,但在当前Message中是唯一的;
3. portType元素的子元素operation定义了方法名, operation元素的属性name值即为方法名称;
4. binging元素的name属性值与portType元素的属性name值一致。binging元素的子元素soap:binging用于创建绑定,用于绑定portType元素的所有子元素operation;
5. service元素的属性name值与WSDL根元素definitions的属性name值一致。service元素的子元素port的属性binging值与第四条bingding元素属性name值一致。
WSDL文档整体定义顺序按:函数内变量数据类型 → 函数方法及形参 → 类文件所有函数名 → 绑定类文件所有函数名 → 后台接口
三、本地测试
注:本地测试的四个文件EvaluateReport.wsdl、client.php、server.php、EvaluateReport.php 都放在WSDL目录下,配置域名www.adata.com【域名需做映射,访问127.0.0.1】跟路径指向WSDL的上级目录即可。
1. EvaluateReport.wsdl
<?xml version='1.0' encoding='utf-8'?>
<wsdl:definitions name="ServiceEvaluateWebServiceImplService"
targetNamespace="http://www.robdchina.com"
xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.robdchina.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- types中定义了函数使用的变量及数据类型 -->
<wsdl:types>
<xs:schema targetNamespace="http://www.robdchina.com" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- 定义一个数组返回值变量returnData -->
<xs:complexType name="returnData">
<xs:sequence>
<xs:element minOccurs="0" name="INFO" type="xs:string"></xs:element>
<xs:element minOccurs="0" name="STATUS" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
<!-- 定义一个数组变量variableNameOne -->
<xs:complexType name="variableNameOne">
<xs:sequence>
<xs:element minOccurs="0" name="KEYONE" type="xs:string"></xs:element>
<xs:element minOccurs="0" name="KEYTWO" type="xs:string"></xs:element>
<xs:element minOccurs="0" name="KEYTHREE" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
<!-- 定义一个数组变量variableNameTwo -->
<xs:complexType name="variableNameTwo">
<xs:sequence>
<xs:element minOccurs="0" name="KEYONE" type="xs:string"></xs:element>
<xs:element minOccurs="0" name="KEYTWO" type="xs:string"></xs:element>
<xs:element minOccurs="0" name="KEYTHREE" type="xs:string"></xs:element>
<xs:element minOccurs="0" name="KEYFOUR" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="emailAddress">
<xs:restriction base="xs:string">
<xs:pattern value=".+@.+"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="number">
<xs:restriction base="xs:int">
<xs:pattern value="\d+" />
</xs:restriction>
</xs:simpleType>
<!-- 定义一个数组变量variableNameThree -->
<xs:complexType name="variableNameThree">
<xs:complexContent>
<xs:extension base="tns:variableNameTwo">
<xs:sequence>
<xs:element name="KEYFIVE" type="tns:emailAddress"/>
<xs:element name="KEYSIX" type="tns:number"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- 定义一个异常变量Exception -->
<xs:element name="Exception" type="tns:Exception"></xs:element>
<xs:complexType name="Exception">
<xs:sequence>
<xs:element minOccurs="0" name="message" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<!-- message定义类文件使用的方法名 -->
<wsdl:message name="functionNameOne">
<wsdl:part name="VariableNameOne" type="tns:variableNameOne">
</wsdl:part>
</wsdl:message>
<wsdl:message name="functionNameOneResponse">
<wsdl:part name="return" type="tns:returnData">
</wsdl:part>
</wsdl:message>
<wsdl:message name="functionNameTwo">
<wsdl:part name="VariableNameTwo" type="tns:variableNameTwo">
</wsdl:part>
</wsdl:message>
<wsdl:message name="functionNameTwoResponse">
<wsdl:part name="return" type="tns:returnData">
</wsdl:part>
</wsdl:message>
<wsdl:message name="functionNameThree">
<wsdl:part name="VariableNameThree" type="tns:variableNameThree">
</wsdl:part>
</wsdl:message>
<wsdl:message name="functionNameThreeResponse">
<wsdl:part name="return" type="tns:returnData">
</wsdl:part>
</wsdl:message>
<wsdl:message name="Exception">
<wsdl:part element="tns:Exception" name="Exception">
</wsdl:part>
</wsdl:message>
<!-- protType将message中的方法名做成逻辑操作 -->
<wsdl:portType name="RobotWebService">
<wsdl:operation name="functionNameOne">
<wsdl:input message="tns:functionNameOne" name="functionNameOne"></wsdl:input>
<wsdl:output message="tns:functionNameOneResponse" name="functionNameOneResponse"></wsdl:output>
<wsdl:fault message="tns:Exception" name="Exception"></wsdl:fault>
</wsdl:operation>
<wsdl:operation name="functionNameTwo">
<wsdl:input message="tns:functionNameTwo" name="functionNameTwo"></wsdl:input>
<wsdl:output message="tns:functionNameTwoResponse" name="functionNameTwoResponse"></wsdl:output>
<wsdl:fault message="tns:Exception" name="Exception"></wsdl:fault>
</wsdl:operation>
<wsdl:operation name="functionNameThree">
<wsdl:input message="tns:functionNameThree" name="functionNameThree"></wsdl:input>
<wsdl:output message="tns:functionNameThreeResponse" name="functionNameThreeResponse"></wsdl:output>
<wsdl:fault message="tns:Exception" name="Exception"></wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<!-- bingding描述如何将portType 映射成传输/消息传递协议 -->
<wsdl:binding name="EvaluateWebServiceSoapBinding" type="tns:RobotWebService">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>
<wsdl:operation name="functionNameOne">
<soap:operation soapAction="" style="rpc"></soap:operation>
<wsdl:input name="functionNameOne">
<soap:body namespace="http://www.robdchina.com" use="literal"></soap:body>
</wsdl:input>
<wsdl:output name="functionNameOneResponse">
<soap:body namespace="http://www.robdchina.com" use="literal"></soap:body>
</wsdl:output>
<wsdl:fault name="Exception">
<soap:fault name="Exception" use="literal"></soap:fault>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="functionNameTwo">
<soap:operation soapAction="" style="rpc"></soap:operation>
<wsdl:input name="functionNameTwo"><soap:body namespace="http://www.robdchina.com" use="literal"></soap:body></wsdl:input>
<wsdl:output name="functionNameTwoResponse"><soap:body namespace="http://www.robdchina.com" use="literal"></soap:body></wsdl:output>
<wsdl:fault name="Exception"><soap:fault name="Exception" use="literal"></soap:fault></wsdl:fault>
</wsdl:operation>
<wsdl:operation name="functionNameThree">
<soap:operation soapAction="" style="rpc"></soap:operation>
<wsdl:input name="functionNameThree"><soap:body namespace="http://www.robdchina.com" use="literal"></soap:body></wsdl:input>
<wsdl:output name="functionNameThreeResponse"><soap:body namespace="http://www.robdchina.com" use="literal"></soap:body></wsdl:output>
<wsdl:fault name="Exception"><soap:fault name="Exception" use="literal"></soap:fault></wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<!-- service列出某个特定绑定的连接信息 -->
<wsdl:service name="ServiceEvaluateWebServiceImplService">
<wsdl:port binding="tns:EvaluateWebServiceSoapBinding" name="EvaluateWebServiceImplPort">
<!-- location为后台要调用的接口 -->
<soap:address location="http://www.adata.com:80/WSDL/server.php"></soap:address>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
2. 客户端client.php
<?php
$client = new SoapClient("EvaluateReport.wsdl", array(
'trace' => 1,
'cache_wsdl' => WSDL_CACHE_NONE
));
try {
//打印暴露的方法
echo "<pre>";
print_r($client->__getFunctions());
echo "</pre>";
//打印对应方法的参数和参数类型
echo "<pre>";
print_r($client->__getTypes());
echo "</pre>";
echo "<hr>";
///调用第一个类方法functionNameOne//
$arrParams = [
'KEYONE' => '第一个键值',
'KEYTWO' => '第二个键值',
'KEYTHREE' => '第三个键值',
];
//第一种调用方法
$arrResultOne = $client->__soapCall('functionNameOne', [$arrParams]);
//第二种调用方法
//$arrResultOne = $client->receiveTaskResult($arrParams);
echo "<pre>";
print_r($arrResultOne);
echo "</pre>";
echo "<hr>";
///调用第二个类方法functionNameTwo//
$arrParams = [
'KEYONE' => '第一个键值',
'KEYTWO' => '第二个键值',
'KEYTHREE' => '第三个键值',
'KEYFOUR' => '第四个键值',
];
$arrResultTwo = $client->__soapCall('functionNameTwo', [$arrParams]);
echo "<pre>";
print_r($arrResultTwo);
echo "</pre>";
///调用第三个类方法functionNameThree//
$arrParams = [
/*'KEYONE' => '第一个键值',
'KEYTWO' => '第二个键值',
'KEYTHREE' => '第三个键值',
'KEYFOUR' => '第四个键值',*/
'KEYFIVE' => '第五个键值',
'KEYSIX' => '第六个键值',
];
$arrResultThree = $client->__soapCall('functionNameThree', [$arrParams]);
echo "<pre>";
print_r($arrResultThree);
echo "</pre>";
}catch (SoapFault $f){
echo "Error Message: {$f->getMessage()}";
//問題1:Error Message: Procedure 'functionNameThree' not present
//解決方法:將server的tmp目錄下的wsdl文件刪除
}
3. 服务端server.php
<?php
require_once 'EvaluateReport.php';
$servidorSoap = new SoapServer("EvaluateReport.wsdl");
$servidorSoap->setClass('EvaluateReport');
$servidorSoap->handle();
4. 类文件EvaluateReport.php
<?php
class EvaluateReport
{
private $returnData = [
'INFO' => 'info',
'STATUS' => 'status',
];
private $exception = 'exception msg';
public function __construct()
{
}
/**
* 第一个类方法
* @access public
* @author GPS
* @param
* @return
* @time 2018-05-09
*/
public function functionNameOne($variableNameOne)
{
foreach ($variableNameOne as $key => $item) {
file_put_contents(__DIR__ . "/WSDL.txt", $key . '--'.$item.PHP_EOL, FILE_APPEND);
}
$jsonData = json_encode($variableNameOne, JSON_UNESCAPED_UNICODE);
file_put_contents(__DIR__ . "/WSDL.txt", time().PHP_EOL, FILE_APPEND);
return $returnData = [
'INFO' => $jsonData,
'STATUS' => 'status',
];
}
/**
* 第二个类方法
* @access public
* @author GPS
* @param
* @return
* @time 2018-05-09
*/
public function functionNameTwo($variableNameTwo)
{
foreach ($variableNameTwo as $key => $item) {
file_put_contents(__DIR__ . "/WSDL.txt", $key . '--'.$item.PHP_EOL, FILE_APPEND);
}
$jsonData = json_encode($variableNameTwo, JSON_UNESCAPED_UNICODE);
file_put_contents(__DIR__ . "/WSDL.txt", time().PHP_EOL, FILE_APPEND);
return $returnData = [
'INFO' => $jsonData,
'STATUS' => 'status',
];
}
/**
* 第三个类方法
* @access public
* @author GPS
* @param
* @return
* @time 2018-05-09
*/
public function functionNameThree($variableNameThree)
{
foreach ($variableNameThree as $key => $item) {
file_put_contents(__DIR__ . "/WSDL.txt", $key . '--'.$item.PHP_EOL, FILE_APPEND);
}
$jsonData = json_encode($variableNameThree, JSON_UNESCAPED_UNICODE);
file_put_contents(__DIR__ . "/WSDL.txt", time().PHP_EOL, FILE_APPEND);
return $returnData = [
'INFO' => $jsonData,
'STATUS' => 'status',
];
}
public function Exception($exception)
{
return $this->exception;
}
}