WebService 服務開發


開發 WebService 服務首先需要根據接口的要求編寫相關的 wsdl 文件。編寫 wsdl 文件需要先對 XML 語法、XML Schema 語法以及 SOAP 語法有一些簡單了解。

假設需要提供一個 Student 服務,該服務僅提供一個操作:add($student),通過 add 方法添加學生信息。即需要定義一個 Student 類,該類僅包括一個方法 add,該方法的參數是一個一維數組,數組鍵名包括:name(string)/sex(int)/age(int)。現在開始定義對應的 wsdl 文件。

wsdl 文件基本結構

wsdl 文件的結構大致如下:

 1 <definitions>
 2     <types>
 3         definition of types........
 4     </types>
 5 
 6     <message>
 7         definition of a message....
 8     </message>
 9 
10     <portType>
11         <operation>
12             definition of a operation.......
13         </operation>
14     </portType>
15 
16     <binding>
17         definition of a binding....
18     </binding>
19 
20     <service>
21         definition of a service....
22     </service>
23 </definitions>            

主要有五個元素構成:types、message、portType、binding、service。根元素是 definitions。

第一步:聲明 wsdl 文件
wsdl 文件本質仍是一個 XML 文件,所以第一行進行 XML 文件聲明:
<?xml version="1.0" encoding="UTF-8"?>
第二步:定義 definitions 根元素
定義 definitions 元素時,需要定義之后將要使用的命名空間及其他屬性。
<wsdl:definitions
    name="StudentService"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://example.com/student/index.php"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://example.com/student/index.php"
>
...
</wsdl:definitions>

xmlns:soap、xmlns:wsdl、xmlns:xsd 這三個命名空間是默認的命名空間,其他默認的命名空間還有:

命名空間
wsdl http://schemas.xmlsoap.org/wsdl/
soap
http http://schemas.xmlsoap.org/wsdl/http/
mime http://schemas.xmlsoap.org/wsdl/mime/
soapenc http://schemas.xmlsoap.org/soap/encoding/
soapenv http://schemas.xmlsoap.org/soap/envelope/
xsi http://www.w3.org/2000/10/XMLSchema-instance
xsd http://www.w3.org/2000/10/XMLSchema

 

 

 

 

 

 

 

 

 

 

 

 

  1. targetNamesapce:指定該服務所屬的命名空間,作用相當於 Java 中的 package。防止與其他 wsdl 文件沖突。一般指定為服務地址即可。
  2. xmlns:soap:定義 soap 命名空間,指定該服務遵守的 soap 版本。
  3. xmlns:wsdl:定義 wsdl 命名空間。該服務遵守的 wsdl 規范。注意:如果不定義該命名空間,則需要定義一個默認命名空間,即xmlns="http://schemas.xmlsoap.org/wsdl/"。定義該命名空間后,所有的 wsdl 元素都需要加載 wsdl 前綴。
  4. xmlns:xsd:定義 xsd 命名空間。XML Schema 實例遵守的規范。
  5. xmlns:tns:自定義命名空間。tns 即 targetNamespace,該文件中定義的新元素屬於該命名空間。一般定義為服務地址即可。
第三步:定義 types 元素
當服務需要的數據類型超出 XML 內置的數據類型范圍時,需要在 types 元素內通過 XML Schema 定義新的數據類型。
根據 Student 服務 add 方法的需求,需要定義 addRequest 數據類型表示請求參數的結構,定義 addResponse 數據類型表示響應消息的結構。
add 方法接受一個一維數組作為參數,鍵名為 name、sex、age。返回一個一位數組,鍵名 msg。
所以 types 元素的結構為:
<wsdl:types>
<!--xsd:schema元素的targetNamespace屬性必需,且值與definitions元素的xmlns:tns相同-->
  <xsd:schema  
    targetNamespace="http://example.com/student/index.php">     <!--定義addRequest數據類型,屬於tns命名空間-->     <xsd:element name="addRequest">       <xsd:complexType>         <xsd:sequence>           <xsd:element name="name" type="xsd:string" minOccurs="0" />           <xsd:element name="sex" type="xsd:string" minOccurs="0" />           <xsd:element name="age" type="xsd:string" minOccurs="0" />         </xsd:sequence>       </xsd:complexType>     </xsd:element>     <!--定義addResponse數據類型,屬於tns命名空間-->     <xsd:element name="addResponse">       <xsd:complexType>         <xsd:sequence>           <xsd:element name="msg" type="xsd:string" minOccurs="1" />         </xsd:sequence>       </xsd:complexType>     </xsd:element>   </xsd:schema> </wsdl:types>

<xsd:schema>元素中必須指定 targetNamespace 屬性,值與definitions元素的xmlns:tns屬性值相同。表示定義的新元素的隸屬命名空間。

第四步:定義 message 元素
每一個 WebService 服務都有都需要定義兩個 message 元素:input 和 output。input 用於描述 WebService 服務接收的參數,output 描述 WebService 服務響應消息。
每一個 message 元素由 0 或多個 part 元素構成。一個 part 元素表示一個請求或響應參數。
part 元素有兩種寫法:
  1. <part name="fristParam" type="xsd:string" />:描述基本類型的參數。
  2. <part name="secondParam" element="tns:addRequest" />:描述復雜類型的參數。
根據服務示例定義 message 元素:
<!--input message:addRequest message,隸屬命名空間tns,數據類型是XML Schema中定義的addRequest-->
<wsdl:message name="addRequest">
  <wsdl:part element="tns:addRequest" name="request" />
</wsdl:message>
<!--output message:addResponse message,隸屬命名空間tns,數據類型是XML Schema中定義的addResponse-->
<wsdl:message name="addResponse">
  <wsdl:part element="tns:addResponse" name="response" />
</wsdl:message>
第五步:定義 portType 元素
portType 元素描述了一個完整的 WebService 服務操作。通過 portType 元素定義不同的 port,用於 binding 元素的綁定操作。這些類型的 port 屬於 tns 命名空間。
  • 一個 portType 元素有一個或多個 operation 元素構成。
  • 一個 operation 元素表示一個 WebService 服務,通過 operation 元素的 name 屬性定義對應的 WebService 服務操作。
  • 一個 operation 元素通常包括一個 input 元素 和一個 output 元素。
根據服務示例定義 portType 元素:
<!--name屬性定義portType-->
<wsdl:portType name="StudentService">
  <!--該operation元素對應add服務-->
  <wsdl:operation name="add">
    <!--input的消息類型是tns:addRequest-->
    <wsdl:input name="addInput" message="tns:addRequest" />
    <!--output的消息類型是tns:addResponse-->
    <wsdl:output name="addOutput" message="tns:addResponse" />
  </wsdl:operation>
</wsdl:portType>
第六步:定義 binding 元素
binding 元素描述了 WebService 服務如何綁定到消息協議,包括指定數據傳輸方式(HTTP GET/HTTP POST/SOAP),指定傳輸協議,指定服務地址等。
binding 元素有兩個屬性:name 和 type。name 屬性定義 binding 元素名稱,type 屬性定義該 binding 指向哪一個類型的 port,即之前定義 portType 元素。type 值即 portType 元素的 name 值。
WSDL1.1 中引入了 soap 的擴展元素:soap:binding/soap:operation/soap:body。binding 元素主要由這三個元素構成。
  1. soap:binding:包括 style 屬性和 transport 屬性。style 屬性定義了 soap 消息格式的整體樣式,值為 rpc 或 document。這兩個值僅表示兩種不同的將 WSDL binding 轉換為 SOAP message 的方式,選擇任一種即可。transport 屬性指定傳輸協議,值為http://schemas.xmlsoap.org/soap/http,表示通過soap HTTP 方式傳輸;值為http://schemas.xmlsoap.org/soap/smtp,表示通過 SOAP SMTP方式傳輸。
  2. soap:operation:將指定的 WebService 服務綁定到指定的 SOAP 實現。屬性 soapAction 指定 soapAction HTTP header 內容以標識對應的服務。
  3. soap:body:包括 use 屬性,值為 encoded 和 literal。指定 input 和 output 的 message 細節。
soap:binding 元素的 style 屬性和 soap:body 元素的 use 屬性組合有四種方式:RPC/encoded、RPC/literal、Document/encoded、Document/literal。其中 RPC/encoded 雖然合法的 WSDL,但和 Document/encoded 一樣並不是 WS-I 的標准規范。所以一般指定 soap:body 元素的 use 屬性值為 literal。如果使用 encoded,需要為 soap:body 元素的 encodedStyle 屬性指定編碼方式,一般為:”http://schemas.xmlsoap.org/soap/encoding/“。

注意:這四種組合的具體差別參考:https://www.ibm.com/developerworks/library/ws-whichwsdl/

 根據服務示例定義 binding 元素:

<wsdl:binding name="StudentServiceBinding" type="tns:StudentService">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="add">     <soap:operation soapAction="http://example.com/student/index.php#add" />     <wsdl:input name="addInput">       <soap:body use="literal" />     </wsdl:input>     <wsdl:output name="addOutput">       <soap:body use="literal" />     </wsdl:output>   </wsdl:operation> </wsdl:binding>
第七步:定義 service 元素
service 元素定義 WebService 支持的 port。每一個支持的協議都有一個對應的 port 元素。service 元素就是一系列 port 元素的集合。
service 元素內的 port 元素的 binding 屬性關聯 binding 元素的 name 屬性。
service 元素內的 port 元素內的 soap:address 元素的 location 屬性是 WebService 服務的地址。
根據服務示例定義 service 元素:
<wsdl:service name="StudentService">
  <wsdl:port name="StudentServicePort" binding="tns:StudentServiceBinding">
    <soap:address location="http://example.com/student/index.php?wsdl" />
  </wsdl:port>
</wsdl:service>

編寫結束后將文件保存為 student.wsdl。

開發服務端

addRequest message 中僅有一個 part 元素,表示 add 方法只有一個參數。定義 Student 類:
Student.php:
class Student
{
  public function add($student)
  {
    return [ 'msg'=>json_encode($studentList, JSON_UNESCAPED_UNICODE)];
  }
}

定義 index.php:

require './Student.php';
if(isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST')
{
  try
  {
    $wsdl = './student.wsdl';
    $soap = new SoapServer($wsdl);
    $soap->setClass('Student');
    $soap->handle();
  }catch (SoapFault $fault){
    echo $fault->getMessage();
  }
}elseif(isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['QUERY_STRING']) == 'wsdl' && $_SERVER['REQUEST_METHOD'] == 'GET')
{
  header('Content-type: text/xml; charset=utf-8');
  $wsdl = file_get_contents('./student.wsdl');
  echo $wsdl;
}else
{
  echo 'No wsdl xml file';
}

客戶端調用

client.php:
try
{
    $soap = new SoapClient('http://127.0.0.1:8091/exercise/soap/index.php?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]);
  $studentInfo = ['name' => 'a', 'sex'=>'0', 'age'=>'20'];
  $backlog = $soap->add($studentInfo);
  var_dump($backlog);
}catch(Exception $e)
{
  var_dump($e->getMessage());
}

不同類型傳參時的 message 元素定義

示例一:請求參數和響應數據都是基本類型
public function($int1, $str1)
{
  return json_encode(func_get_args());
}
請求參數有兩個,一個是 int,一個是 string。響應消息是一個 string。
message 元素定義:
<wsdl:message name="addRequest">
  <wsdl:part name="int1" type="xsd:int" />
  <wsdl:part name="str1" type="xsd:string" />
</wsdl:message>
<wsdl:message name="addResponse">
  <wsdl:part name="return" type="xsd:string" />
</wsdl:message>
此時不需要再定義 types 元素。因為請求和響應的數據都是基本類型,不需要定義額外的數據類型。
客戶端調用:
$soap = new SoapClient('http://192.168.12.118:8091/exercise/soap/index.php?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]);
$backlog = $soap->add(20, 'age');
var_dump($backlog);

示例二:請求參數是長度不定的一維數組

public function($idList)
{
  return json_encode($idList);
}
因為數組已經不是 XML 內置的基本數據類型了,因此需要在 types 元素內通過 XML Schema 定義一個數組的數據類型。
types 元素定義:
<wsdl:types>
  <xsd:schema
  targetNamespace="http://example.com/student/index.php">
    <!--定義addRequest數據類型,屬於tns命名空間-->
    <xsd:element name="addRequest">
      <xsd:complexType>
        <xsd:sequence>
          <!--maxOccurs="unbounded"屬性表示該元素出現的最大次數不定,通過該屬性定義一個長度不定的數組-->
          <xsd:element name="id" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
</wsdl:types>

message 元素定義:

<wsdl:message name="addRequest">
  <wsdl:part name="idList" element="tns:addRequest" />
</wsdl:message>
<wsdl:message name="addResponse">
  <wsdl:part name="return" type="xsd:string" />
</wsdl:message>

客戶端調用:

$soap = new SoapClient('http://192.168.12.118:8091/exercise/soap/index.php?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]);
$backlog = $soap->add([1,2,3]);
此時服務端接收的數據結構是:["id":["1","2","3"]],一個二維數組,二維數組包含一個鍵名為“id”的元素,該元素就是客戶端傳過來的一維數組。
也可以以對象類型調用:
$std = new stdClass();
$std->id = [1, 2, 3];
$backlog = $soap->add($std);

此時服務端接收到的數據格式與之前的相同。

注意 當客戶端的請求參數是只有一個元素的一維數組時,服務端接收的數據不再是一個二維數組,也是一個一維數組。

客戶端調用:

$backlog = $soap->add([1]);

服務端接口數據為:["id":"1"]

示例三:傳入多維數組
現在設定 add 方法接受一個參數,該參數是一個多為數組,結構如下:
[
  "num": "傳入的學生信息數量",
  "studentList": [
    ["name":1, "age":20, "sex": 0],
    ["name":2, "age":20, "sex": 0],
    ...
  ]
]

服務端方法:

public function($studentInfo)
{
  return json_encode($studentInfo);
}

types 元素和 message 元素的定義:

<wsdl:types>
  <xsd:schema
  targetNamespace="http://example.com/student/index.php">
    <xsd:element name="addRequest">
      <xsd:complexType>
        <xsd:sequence>
          <xsd:element name="num" type="xsd:int" />
          <xsd:element name="studentList" type="tns:studentList" />
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
    <!--定義studentList數據結構,長度不定的數組-->
    <xsd:complexType name="studentList">
      <xsd:sequence>
        <xsd:element name="student" type="tns:student" minOccurs="0" maxOccurs="unbounded" />
      </xsd:sequence>
    </xsd:complexType>
    <!--定義student數據結構-->
    <xsd:complexType name="student">
      <xsd:sequence>
        <xsd:element name="name" type="xsd:string" minOccurs="0" />
        <xsd:element name="age" type="xsd:string" minOccurs="0" />
        <xsd:element name="sex" type="xsd:string" minOccurs="0" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:schema>
</wsdl:types>

<wsdl:message name="addRequest">
  <wsdl:part name="studentInfo" element="tns:addRequest" />
</wsdl:message>
<wsdl:message name="addResponse">
  <wsdl:part name="return" type="xsd:string" />
</wsdl:message>
定 add 方法接受一個參數,該參數是一個多為數組,結構如下:

客戶端調用:

$soap = new SoapClient('http://192.168.12.118:8091/exercise/soap/index.php?wsdl', ['trace'=>true, 'cache_wsdl'=>WSDL_CACHE_NONE]);
$studentInfo = ['name' => 'a', 'sex'=>'0', 'age'=>'20'];
$list = [
  'num' => 2,
  'studentList' => [$studentInfo, $studentInfo],
];
$backlog = $soap->add($list);

注意:傳入請求參數時,務必保持參數的傳入順序和 input message 中的 part 元素順序一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM