我正在构建一个使用肥皂的应用程序。
我使用 Zend_Soap_Autodiscover 生成 WSDL,使用 SoapClient 和 SoapServer 处理请求。
我遇到的问题是客户端不断给出 VersionMismatch,即使服务器和客户端都设置为soap 1.1版。
我不知道如何解决这个问题,并且已经坚持了一段时间。因此,任何帮助都会非常感激。
我正在尝试在客户端调用更新方法:
public function update() {
$this->_soap = Soap_Client_General::getInstance();
try {
$this->_soap->fetchNewVersion();
} catch (Exception $e) {
var_dump($e);
}
}
然而,这给了我以下转储:
object(SoapFault)#12 (10) {
["message":protected]=>
string(13) "Wrong Version"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(59) "/path/app/library/Update/Client.php"
["line":protected]=>
int(27)
["trace":"Exception":private]=>
array(4) {
[0]=>
array(6) {
["file"]=>
string(59) "/path/app/library/Update/Client.php"
["line"]=>
int(27)
["function"]=>
string(6) "__call"
["class"]=>
string(10) "SoapClient"
["type"]=>
string(2) "->"
["args"]=>
array(2) {
[0]=>
string(15) "fetchNewVersion"
[1]=>
array(0) {
}
}
}
[1]=>
array(6) {
["file"]=>
string(59) "/path/app/library/Update/Client.php"
["line"]=>
int(27)
["function"]=>
string(15) "fetchNewVersion"
["class"]=>
string(10) "SoapClient"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[2]=>
array(6) {
["file"]=>
string(55) "/path/app/application.class.php"
["line"]=>
int(25)
["function"]=>
string(6) "update"
["class"]=>
string(13) "Update_Client"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[3]=>
array(6) {
["file"]=>
string(39) "/path/index.php"
["line"]=>
int(24)
["function"]=>
string(2) "go"
["class"]=>
string(11) "Application"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
}
["previous":"Exception":private]=>
NULL
["faultstring"]=>
string(13) "Wrong Version"
["faultcode"]=>
string(15) "VersionMismatch"
["faultcodens"]=>
string(41) "http://schemas.xmlsoap.org/soap/envelope/"
}
我的服务器的肥皂处理发生在我的 Zend 框架应用程序的一个肥皂模块中。此模块中的所有控制器都从一个基本控制器类扩展而来。
My_Controller_Base_Soap
<?php
class My_Controller_Base_Soap extends My_Controller_Base {
protected $_uri;
public function __construct(\Zend_Controller_Request_Abstract $request, \Zend_Controller_Response_Abstract $response, array $invokeArgs = array()) {
parent::__construct($request, $response, $invokeArgs);
$this->_uri = $this->view->serverUrl() . $this->view->baseUrl('soap/');
//Disable Layout
$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender();
Application_Model_Log::log('Soap Module Called: ' . implode('-', $request->getParams()) . ' from: '. $_SERVER['REMOTE_ADDR'] , 'soap');
}
//Generates WSDL for given class
protected function handleWSDL($classname) {
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setClass($classname);
$autodiscover->handle();
}
//Handles the soap requests for the giving location and classname
protected function handleSOAP($loc, $classname) {
try {
$options = array('uri' => 'urn:' . $classname,
'location' => $loc,
'soap_version' => SOAP_1_1,
'cache_wsdl' => WSDL_CACHE_NONE);
$server = new SoapServer(null, $options);
$server->setClass($classname);
$server->handle();
} catch (Exception $e) {
Application_Model_Log::log('SoapException: ' . $e->getMessage(), 'soap');
}
}
}
上面的类从 My_Controller_Base 扩展而来,而 My_Controller_Base 又从 Zend_Controller_Action 扩展而来
肥皂/通用控制器
<?php
class Soap_GeneralController extends My_Controller_Base_Soap {
const CLASS_OBJECT = 'Soap_Model_General';
public function indexAction() {
$this->handleSOAP($this->view->baseUrl('soap/general/wsdl'), self::CLASS_OBJECT);
}
public function wsdlAction() {
$this->handleWSDL(self::CLASS_OBJECT);
}
}
Zend_Soap_Autoloader 使用一个类来生成它的 wsdl:
Soap_Model_General
<?php
class Soap_Model_General extends Soap_Model_Base_Abstract{
/**
* Retrieve new Version
*
* @return string
*/
public function fetchNewVersion(){
if (!$this->_authenticated)
throw new SOAPFault("Not Authenticated.", 401);
}
}
此类从包含身份验证方法的抽象基类扩展而来。
Soap_Model_Base_Abstract
<?php
abstract class Soap_Model_Base_Abstract {
/**
* This property represents authentication status
* @var boolean
*/
protected $_authenticated = false;
/**
* The site object
* @var Application_Model_Website
*/
protected $_website;
/**
* Authenticates the SOAP request. (This one is the key to the authentication, it will be called upon the server request)
*
* @param array
* @return array
*/
public function authenticate($login) {
if (!empty($login->siteid) && !empty($login->hash)) {
//Determain if credentials are correct
$found = false;
$website = Application_Model_Website::find($login->siteid);
if ($website instanceof Application_Model_Site) {
//Is the authentication hash is correct? (siteid, secretkey and remote ip correct?)
$hash = md5($website->id . $website->secretkey . $website->ip);
if ($hash == $login->hash) {
$found = true;
}
}
//Verify the send IP
if ($found && $website->ip == $_SERVER['REMOTE_ADDR']) {
//Accept authentication
$this->_authenticated = true;
//set Application_Model_Site to site property
$this->_website = $website;
return true;
} else {
throw new SOAPFault("Incorrect credentials.", 401);
}
} else {
throw new SOAPFault("Invalid authentication format. Values may not be empty and are case-sensitive.", 401);
}
}
}
所以基本上,当我接近我的 WSDL 时,我得到了一个完美生成的 WSDL。
接下来就是将客户端连接到服务器。
客户的代码在这里:
Soap_Client_General
<?php
class Soap_Client_General extends Soap_Client_Abstract {
private static $instance = NULL;
public static function getInstance() {
//get config
$config = Zend_Registry::get('config');
$wsdl = $config->general->server->general . '/wsdl/';
if (!self::$instance) {
return parent::getInstance($wsdl);
}
return self::$instance;
}
}
Soap_Client_Abstract
<?php
abstract class Soap_Client_Abstract {
private function __construct() {
}
/**
* Return client instance or create intitial
*
* @return SoapClient
* @access public
*/
public static function getInstance($wsdl) {
$ns = "auth";
$config = Zend_Registry::get('config');
$siteid = $config->general->siteid;
$secretkey = $config->general->secretkey;
$hash = md5($siteid . $secretkey . $_SERVER['SERVER_ADDR']);
//Create our Auth Object to pass to the SOAP service with our values
$auth->siteid = $siteid;
$auth->hash = $hash;
$auth_vals = new SoapVar($auth, SOAP_ENC_OBJECT);
//The 2nd variable, 'authenticate' is a method that exists inside of the SOAP service
$authenticate = new SoapHeader($ns, 'authenticate', $auth_vals, false);
$client = new SoapClient($wsdl, array('cache_wsdl' => WSDL_CACHE_NONE, 'soap_version' => SOAP_1_1));
$client->__setSoapHeaders(array($authenticate));
return $client;
}
private function __clone() {
}
}
我希望我已经提供了足够的信息。我希望你能在这件事上给我一些指导。