以前没做过webservice,现在项目需要,只好边学边做,还好有google大神和baidu大哥帮助。
zf的框架很牛,做webservice基本不用动脑
只用到zend_soap包中的Zend_Soap_Server,Zend_Soap_AutoDiscover和Zend_Soap_Client三个类
首先要注意ZF是调用php的soap扩展,所以请确认php.ini(;extension=php_soap.dll 去掉分号)中打开了soap扩展,同时注意配置php.ini中soap段的wsdl缓存,调试时请关闭该缓存,否则
修改model后无法查看效果。发布时可以把缓存打开。还有就是使用服务器套件的问题,我试过使用APMServ5.2.6,完全正确的代码,就是使用
Zend_Soap_Client时无法获取服务端提供的服务函数,最后改用wapmserver又没有问题,哎。。。
基本流程就是使用使用Zend_Soap_Server,Zend_Soap_AutoDiscover构建服务端,然后使用Zend_Soap_Client来调用服务端提供的功能
基本代码
(1)服务端,先建立controller
/modules/services/controllers/WapSearchControllers.php
<?php
//预加载model类
require_once realpath(APPLICATION_PATH).'/modules/services/models/WapArticle.php';
class Services_WapSearchController extends Zend_Controller_Action
{
private $_WSDL_URI = "http://192.168.1.100/kktapp/public/services/wapsearch/index?wsdl";
private function handleWSDL() {
$autodiscover = new Zend_Soap_AutoDiscover();
//类名要注意一致
$autodiscover->setClass('WapArticle');
$autodiscover->handle();
}
private function handleSOAP() {
$soap = new Zend_Soap_Server($this->_WSDL_URI);
$soap->setClass('WapArticle');
$soap->handle();
}
//不需要视图和layout,所以禁用之
public function init(){
$this->_helper->viewRenderer->setNoRender();
$this->_helper->layout()->disableLayout();
}
public function indexAction(){
//判断请求中是否有wsdl有就自动生成wsdl的uri否则启动soap服务
if(isset($_GET['wsdl'])) {
$this->handleWSDL();
} else {
$this->handleSOAP();
}
}
//客户端测试
public function clientAction(){
$client = new Zend_Soap_Client($this->_WSDL_URI);
//调用服务端提供的服务
$res = $client->getArticle(31);
var_dump($res);
}
}
需要特别说明的是,这里的setClass成员函数传入的业务逻辑类名称一定要和下面的业务逻辑类名称一致,不然会报非法控制器错误
建完controllers该建model了
//业务逻辑所在层,把所有需要提供的服务都可以放在这一层中
/modules/services/models/models/WapArticle.php
<?php
//预加载其他原来已实现的的业务逻辑或者数据库操作类
require_once realpath(APPLICATION_PATH).'/modules/news/models/FrontDbTable/Article.php';
class WapArticle{
/**
* getArticles ,operate database example
*
* @param Int $id
* @return String
*/
public function getArticle($id)
{
//直接实例化一个原来的数据库操作类
$dbtable = new News_Model_FrontDbTable_Article();
$s = $dbtable->getArticle($id);
//这里可以把结果进行xml格式化或者json格式化,以方便其他客户端调用
$d = json_encode($s);
return $d;
}
/**
* Simple array sort
*
* @param Array $array
* @return Array
*/
public function simple_sort($array) {
asort($array);
return $array;
}
/**
* Adds method
*
* @param Int $param1
* @param Int $param2
* @return Int
*/
public function math_addx($param1, $param2) {
return $param1+$param2;
}
}
?>
这里要特别说明的是:
我曾尝试让WapArticle类直接继承Zend_Db_Table_Abstract类,然后再在WapArticle类中直接对数据库表操作,没有成功,报出非法控制器错误,不知
如何解决,google了一下,好像网上也有类似的问题,不知是ZF本身的问题,还是说我没写对,有哪位大神路过的话,还望给指点一二
然后我又尝试使用Zend_Registry::get('db')获取数据库连接对象,也是为空,又失败,正当我一筹莫展时,突然想起尝试一下在该类中直接实例化一
个原来的的数据库表操作类试试(modules/news/models/FrontDbTable/Article.php),没想到还真成功了。不知道这个问题出在哪里!不过这样也好
。把这个层单独独立出来,只处理业务逻辑,数据库操作在另外一个层实现,倒实现了分离的目的,嘿嘿,算是无心插柳吧。
还有就是业务逻辑层的成员函数说明格式要注意,不然好像还会报出非法控制器错误(哎,啥都报这个错误,还让人活不。。。)
/**
* Adds method
*
* @param Int $param1
* @param Int $param2
* @return Int
*/
public function math_addx($param1, $param2) {
return $param1+$param2;
}
函数名称说明与函数参数说中间有一行空格
输入要采用"@param 参数类型 参数名"的格式
输入要采用"@return 参数类型"的格式
顺便也贴上/modules/news/models/FrontDbTable/Article.php的代码
class News_Model_FrontDbTable_Article extends Zend_Db_Table_Abstract
{
protected $_name = 'custom_article';
public function getArticle($id)
{
$id = (int)$id;
$where = array('id='.$id, 'isshow=1');
$row = $this->fetchRow($where);
if(!$row){
return 0;
}
return $row->toArray();
}
}
下面就是如何使用了
有三个地址
http://192.168.1.100/kktapp/public/services/wapsearch/index?wsdl显示该server的wsdl,uri其中对服务做了详细描述包括服务名称,服务的类
型,输入输出参数等
而http://192.168.1.100/kktapp/public/services/wapsearch/index则可查看服务是否正常运行
一般出现如下界面就说明服务正常运行
<?xml version="1.0" encoding="UTF-8" ?>
- <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
- <SOAP-ENV:Body>
- <SOAP-ENV:Fault>
<faultcode>Sender</faultcode>
<faultstring>Invalid XML</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
http://192.168.1.100/kktapp/public/services/wapsearch/client则是客户端测试,当然也可以使用其他客户端(如java,.net等)进行测试
这里只测试了一个服务getArticle($id),数据库用户配置正确的话,应该返回一个json格式的数组
-----------------------------------------------------------------------------
用java写了客户端,测试了一下,还行能调用
使用到了axis1.4代码如下
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
public class Testserver {
public static void main(String[] args) {
try {
String endpoint = "http://192.168.1.100/kktapp/public/services/wapsearch/index";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(endpoint);
call.setOperationName("getArticle");
int temp = 31;
String result = (String) call.invoke(new Object[] { temp });
System.out.println("result is " + result);
}
catch (Exception e) {
System.err.println(e.toString());
}
}
}