返回

NuSOAP Web Service 传递数组详解:服务端与客户端的配置

php

使用 NuSOAP 传递数组到 Web Service

当尝试使用 NuSOAP 构建的 PHP Web Service 传递数组时,可能会遇到客户端接收空数组作为响应的问题。 这个问题通常源于服务端对复杂数据类型定义的不完整或者客户端发送数据的方式不匹配。核心在于 Web Service 如何理解和处理传递的数组结构,正确定义复杂数据结构以及客户端与服务端的参数映射非常重要。

问题分析

问题表现: 客户端发送数组,服务端函数未正确接收数据,导致计算结果错误或返回空数组。
根本原因: 缺乏对传递数组的类型定义或者客户端传递的数据格式和服务端期望的数据结构不匹配。服务端通过 WSDL 文件了可用的操作以及所需的数据结构,当客户端传递的数据与 WSDL 定义不匹配时,服务可能无法正确解析数据。
解决思路: 确保服务端的 WSDL 定义完整且准确,特别是对复杂类型的定义(如数组或结构体),并确保客户端发送的数据格式与服务端的 WSDL 定义匹配。

解决方案 1:定义请求的结构体 (Struct)

问题表现: 服务端代码缺失对于客户端传递数组的结构体的定义。即使发送一个键值对的数组,如果没有明确定义其类型,服务端也无法正确解析,导致传递过去的内容变成空数组,也就出现响应也是空数组的问题。

原理: NuSOAP 需要预先定义复杂的数据类型(比如数组或结构体),用于 WSDL (Web Services Description Language) 的定义。 这样,服务端才能明白客户端传来的数据类型以及数据的结构,能够正确解析和使用。 当数组或对象作为 Web Service 函数的参数传递时,通常使用“struct”来对象的结构,struct定义了每一个键对应的数据类型。

操作步骤:

  1. 服务端需要在 WSDL 定义中,通过 addComplexType 定义一个“struct”来描述数组(或更准确的,代表一个记录的结构体),包含数组内的每个元素的键和类型。这个结构体定义了服务端接收数组元素的基础。
  2. register方法中,需使用'tns:ArrayReq' 来声明服务端函数所期待的结构体数据,表示方法需要一个特定结构的数组(其实是这个预先定义的结构体对象),参数必须以定义好的结构体的键作为传入数组的键。

服务端代码示例 (已修改):

<?php
require_once('c:\\wamp\\www\\nusoap.php');
$server = new soap_server();
$server->wsdl->schemaTargetNamespaces = 'urn:GetArr';
$server->configureWSDL('GetArr','urn:GetArr');

// 定义产品结构体(记录结构体)
$server->wsdl->addComplexType(
'Product',
'complexType',
'struct',
'all',
'',
array(
'name' => array('name' => 'name', 'type' => 'xsd:string'),
'code' => array('name' => 'code', 'type' => 'xsd:string'),
'price' => array ('name' => 'price', 'type' => 'xsd:int'),
'quantity' => array ('name' => 'quantity', 'type' => 'xsd:int'),
'total_price' => array('name' => 'total_price', 'type' => 'xsd:int')
));

//定义客户端请求的结构体
$server->wsdl->addComplexType(
'ArrayReq',
'complexType',
'struct',
'all',
'',
array(
'name' => array('name' => 'name', 'type' => 'xsd:string'),
'code' => array('name' => 'code', 'type' => 'xsd:string'),
'price' => array ('name' => 'price', 'type' => 'xsd:int'),
'quantity' => array ('name' => 'quantity', 'type' => 'xsd:int')
));


//定义响应返回的数组类型
$server->wsdl->addComplexType(
'ProductArray',
'complexType',
'array',
'',
'SOAP-ENC:Array',
array(),
array(
array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'tns:Product[]')),
'tns:Product');

function GetTotalPrice ($proArray) {
  $temparray = array();
    $temparray[] = array('name' => $proArray['name'], 'code' => $proArray['code'], 'price' => $proArray['price'], 'quantity' => $proArray['quantity'], 'total_price' => $proArray['quantity'] * $proArray['price']);

  return $temparray;
  };

$server->register('GetTotalPrice',
array('proArray' => 'tns:ArrayReq'),
array('return' => 'tns:ProductArray'),
'urn:GetArr',
'urn:GetArr#GetTotalPrice',
'rpc',
'encoded',
'Get the product total price'
);

$post = file_get_contents('php://input');
$server->service($post);
?>

代码解析:

  • addComplexType 创建了一个名为ArrayReq的复杂类型,用来接收客户端发来的数组对象。
  • 参数的'tns:ArrayReq'表示此服务接口需要的参数是一个ArrayReq结构的,服务端的业务代码里,就可以用$proArray['name']的方式取到客户端传入的数据。

解决方案 2:客户端使用正确的参数传递方式

问题表现: 客户端没有使用服务端 WSDL 定义的复杂类型发送参数。即使服务端的ArrayReq结构已经声明,如果客户端传递数据时不是一个预先定义好的ArrayReq类型的参数,也会造成数据无法解析的问题。

原理: NuSOAP 客户端在调用服务端 Web Service 时,需要传递服务端期待的格式的参数,客户端需要明确构造一个包含服务端定义结构的数据发送给服务端,这可以通过把客户端要传递的数据,整理成键值对的形式来实现。服务端接受到客户端传来的数据会根据参数名解析为ArrayReq类型的变量$proArray, 业务代码直接通过对应的键就可以访问客户端传递过来的值。

操作步骤:
1.客户端代码需要按服务端定义的ArrayReq结构构造参数,包含'name'、'code'、'price'、'quantity' 这些键。
2. 客户端调用 Web Service 方法 call的时候,务必将要传递的参数(数组结构体)放到一个包含所有参数的数组里,注意和函数定义的参数名对应。
3.务必使用数组类型包裹要发送给服务端的数组参数。 例如: array($arr) 而不是直接 array($arr)

客户端代码示例 (已修改):

<?php
require_once('c:\\wamp\\www\\nusoap.php');
ini_set ('soap.wsdl_cache_enabled', 0);

$arr['name'] = "GoPro";
$arr['code'] = "245";
$arr['price'] =70;
$arr['quantity'] = 4;

$sClient = new nusoap_client('http://localhost/nusoap/comserv.php?wsdl','wsdl','','','','');
//修改了call方法的调用,用数组包裹$arr
$response = $sClient->call('GetTotalPrice',array('proArray'=>$arr),'','', false,true);

$error = $sClient->getError();
if ($error) {
    echo "<h2>Constructor error</h2><pre>" . $error . "</pre>";
}

if ($sClient->fault) {
    echo "<h2>Fault</h2><pre>";
    echo ($response);
    echo "</pre>";
}
else {
    $error = $sClient->getError();
    if ($error) {
        echo "<h2>Error</h2><pre>" . $error . "</pre>";
    }
if ($response != "" || NULL){
    echo "<h2>Respond</h2><pre>";
    print_r ($response);
    echo "</pre>";
    }
}

?>

代码解析:

  • 修改call方法,第二个参数,从直接传入$arr改成传递一个数组array('proArray'=>$arr),表示以'proArray'为键,$arr为值的形式将数据传递过去。
  • 参数的名称需要和register时服务侧的函数签名参数名对应,即注册服务时需要和 array('proArray' => 'tns:ArrayReq') 这里的 proArray 一致。
  • 客户端需要发送包含“name”、“code”、“price”、“quantity” 键值对数组,对应服务端 ArrayReq 的定义, 这样服务端代码 $temparray[] = array('name' => $proArray['name'], ...) 才可以直接访问到相应的值。

总结

传递数组到 NuSOAP Web Service 需要明确定义复杂数据类型以及服务端客户端使用相同的参数结构和参数名称。 如果遇到问题,可以检查以下步骤:

  1. 检查服务端WSDL :确保在 WSDL 中定义了必要的结构体,这些定义准确地描述了期望的复杂类型。特别注意addComplexTyperegister的参数定义。
  2. 客户端数据结构 : 客户端构造的数据结构,必须与服务端 WSDL 中定义的参数类型匹配,参数键名称也要匹配,传递给服务端的方法时,一定要包裹在包含键的数组中。
  3. 仔细核对 : 对照服务端定义和客户端代码中的参数键和值,以及注册服务时定义的方法签名进行一一检查,务必保持完全一致。
    通过确保数据结构的正确定义以及客户端和服务端参数的有效传递,就可以避免因为数组传递问题带来的麻烦。