Yvision.kz
kk
Разное
Разное
399 773 постов42 подписчика
Всяко-разно
0
23:41, 25 августа 2012

SOAP сервер на Zend Framework с использованием Zend_Soap_AutoDiscover для работы с клиентом написаным на .NET

При работе над одним порталом у нас возникла задача, по обмену данными между порталом и несколькими другими программами написанными на .NET. Разработчики этих программ настаивали сделать взаимодействие при помощи SOAP, мы согласились.

В этой статье хотелось бы раскрыть проблемы возникшие при разработке SOAP сервера на Zend Framework с использованием Zend_Soap_AutoDiscover.

Так как портал был полностью реализован на Zend Framework мы и решили реализовать SOAP сервер на нем. Так же мы решили использовать AutoDiscover что бы не писать wsdl файл в ручную, так как планировалось что в наш сервер будет добавляться различный функционал в будущем. И не хотелось тратить время на описание этих функций в wsdl файле. Заглянув в ман мы нашли код нужный для создания SOAP сервера.

  1. if(isset($_GET['wsdl'])) {
  2. $autodiscover = new Zend_Soap_AutoDiscover();
  3. $autodiscover->setClass('ExcampleService');
  4. $autodiscover->handle();
  5. } else {
  6. // pointing to the current file here
  7. $soap = new Zend_Soap_Server("http://example.com/soap.php?wsdl");
  8. $soap->setClass('ExcampleService');
  9. $soap->handle();
  10. }

Как же все просто подумали мы, но .NET отказался работать с нашим сервером. Мы стали искать проблемы почему не работает наш сервер с .NET программами, когда клиент на PHP успешно обменивался данными с ним.

Проблема первая.

Как мы знаем PHP не типизированный язык, когда .NET типизированный и это первая проблема. Потому что при генерации wsdl AutoDiscover не может определить типы возвращаемых данных. Для решения этой проблемы нужно указывать тип возвращаемых данных в описании функций.

Пример описания:

  1. class ExcampleService {
  2. /**
  3. * Return true if record active, else if record inactive
  4. *
  5. * @param integer $recordId
  6. * @return boolean
  7. */
  8. public function isActiveRecord($recordId) {
  9. $recordsTable = new Model_Records();
  10. return $recordsTable->isActiveRecord($recordId);
  11. }
  12. }

При просмотре wsdl выяснилось что типы простых данных указываются, но если возвращается массив то тип его элементов не указывается. Воспользовавшись поисковиками мы нашли решение. Возвращать не массив а объект с массивом других объектов. Типы всех свойств должны быть указаны в описании этих объектов.

Пример описания классов возвращаемых объектов:

 
  1. class SRecord {
  2. /** @var int */
  3. public $id;
  4. /** @var string */
  5. public $text;
  6. }
  7. class SRecordsArray {
  8. /** @var SRecord[] */
  9. public $records;
  10. }

И код самого класса ExcampleService:

  1. class ExcampleService {
  2. /**
  3. * Return true if record active, else if record inactive
  4. *
  5. * @param integer $recordId
  6. * @return boolean
  7. */
  8. public function isActiveRecord($recordId) {
  9. $recordsTable = new Model_Records();
  10. return $recordsTable->isActiveRecord($recordId);
  11. }
  12. /**
  13. * Return list of records
  14. *
  15. * @return SRecordsArray
  16. */
  17. public function getList() {
  18. $recordsTable = new Model_Records();
  19. $records = $recordsTable -> getList();
  20. $result = array();
  21. foreach ($records as $record) {
  22. $oSrecord = new SRecord();
  23. $oSrecord->id = $record->id;
  24. $oSrecord->text = $record->text;
  25. $result[] = $oSrecord;
  26. }
  27. $oSrecordsArray = new SRecordsArray();
  28. $oSrecordsArray->records = $result;
  29. return $oSrecordsArray;
  30. }
  31. }
 

Сохраняем изменения запускаем программу .NET и видим что она снова отказывается работать.

Проблема вторая.

Проблема в том что .NET поддерживает только один стандарт SOAP WS-I а AutoDiscover по умолчанию использует стандарт XSD поэтому нам нужно привести wsdl к стандарту WS-I. Для этого нам нужно доработать первоначальный код следующим образом.

 
  1. if(isset($_GET['wsdl'])) {
  2. $autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence');
  3. $autodiscover->setBindingStyle(array('style'=>'document'));
  4. $autodiscover->setOperationBodyStyle(array('use' => 'literal'));
  5. $autodiscover->setClass('ExcampleService');
  6. $autodiscover->handle();
  7. } else {
  8. // pointing to the current file here
  9. $soap = new Zend_Soap_Server("http://example.com/soap.php?wsdl");
  10. $soap->setClass('ExcampleService');
  11. $soap->handle();
  12. }

Сохраняем изменения, обновляем сервер, запускаем программу на .NET и что мы видим, программа выполняет все методы сервера но получает от сервера всегда только null.

Проблема третья.

Последняя проблема связанна с получением и передачей данных. Для решения этой проблемы мы написали специальный класс который трансформирует входящие и исходящие данные.

Исходный код класса:

  1. class Service_Soap
  2. {
  3. protected $_service;
  4. public function __construct()
  5. {
  6. $this->_service = new ExcampleService();
  7. }
  8. public function __call($name, $arguments)
  9. {
  10. $params = array();
  11. if(count($arguments) > 0){
  12. foreach($arguments[0] as $property=>$value){
  13. $params[$property] = $value;
  14. }
  15. }
  16. $result = call_user_func_array(array($this->_service, $name), $params);
  17. return array("{$name}Result" => $result);
  18. }
  19. }
 

И доработали код создания сервера

 
  1. if(isset($_GET['wsdl'])) {
  2. $autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence');
  3. $autodiscover->setBindingStyle(array('style'=>'document'));
  4. $autodiscover->setOperationBodyStyle(array('use' => 'literal'));
  5. $autodiscover->setClass('ExcampleService');
  6. $autodiscover->handle();
  7. } else {
  8. // pointing to the current file here
  9. $soap = new Zend_Soap_Server("http://example.com/soap.php?wsdl");
  10. //$soap->setClass('ExcampleService');
  11. $modelSoap = new Service_Soap();
  12. $soap->setObject($modelSoap);
  13. $soap->handle();
  14. }
 

После сохранения программа на .NET заработала и портал стал обмениваться данными с ней.

Надеюсь что этот пост поможет кому то при разработке SOAP сервера. И он уже быстро справится с проблемами описанными в статье, нам решение этих проблем заняло достаточно времени.

Спасибо всем за внимание и удачи вам!

0
738
1