Yii依靠文档注解(doc comments)和类反射(class
reflection)来识别哪个方法可以被远程调用,包括他们的参数和返回值,但目前只能返回字符串,不能返回数组,若返回数组,则为"array";故我将返回值转换为json字符串再返回.
1. 定义Service Provider,服务器端文件代码,WebServerController.php
<?php /****************************** * @author sara zhou * create time 2014-06-25 16:00:00 * YII框架API接口端的server (基于Webservice的接口服务器端) * 注意php.ini开启soap扩展:extension=php_soap.dll ******************************* */ class WebServerController extends CController{ /** * @var array error * @soap */ public $error=array( '0000'=>'操作成功', '0001'=>'操作失败', '0002'=>'参数param错误', '1001'=>'用户不存在', ); public function actions() { return array( 'approval'=>array( 'class'=>'CWebServiceAction', 'classMap'=>array(//classMap其实只要是对应的Model就行,比如这里的ApiUser 'ApiUser', ), ), ); } /** * @desc get_user_info()获取用户信息,代码注释里包含 * @soap,则 此方法可通过soap访问到 * @param array * @return array the ApiUser info 也必须和Model名称一致 * @soap */ public function get_user_info($param_arr){ $param_arr=CJSON::decode($param_arr); $param['user_name']=htmlspecialchars(@$param_arr['user_name']); $param['request_id']=intval(@$param_arr['request_id']); //添加API访问日志信息 $apiLog=new ApiLog1(); $log_id=$apiLog->insert_api_log($param['request_id'], CJSON::encode($param)); $res=array('user_name'=>$param['user_name'],'user_email'=>$param['user_email']); //验证相关参数是否符合要求 if (empty($param['user_name'])) { $res['response_time']=date("YmdHis"); $res['error_code']='0002'; $res['error_msg']=str_replace('param','user_name',$this->error['0002']); //更新API访问日志信息 $apiLog->update_api_log($log_id, CJSON::encode($res),@$res['response_time'], @$res['error_code'],$res['error_msg']); return CJSON::encode($res); } if ($param['request_id']<=0) { $res['response_time']=date("YmdHis"); $res['error_code']='0002'; $res['error_msg']=str_replace('param','request_id',$this->error['0002']); //更新API访问日志信息 $apiLog->update_api_log($log_id, @$res,@$res['response_time'], @$res['error_code'],$res['error_msg']); return $res; } $user=new User(); $user_info=$user->getUserInfo($param['user_name']); if ($user_info) { $res['error_code']="1003"; } else { $res['error_code']="0000"; } $res['response_time']=date("YmdHis"); $res['error_msg']=$this->error[$res['error_code']]; //更新API访问日志信息 $apiLog->update_api_log($log_id, @$res,@$res['response_time'], @$res['error_code'],$res['error_msg']); return $res; } /* * 代码注释里不包含 * @soap,则无法通过soap方式访问 */ public function test($param_arr){ $arr = array( 'user_id'=>$param_arr['user_id'], 'request_id'=>$param_arr['user_id'],//'请求号', 'request_time'=>$param_arr['user_id'],//'请求时间' ); return true; } }
保存WebServerController.php文件,浏览器输入地址:http://localhost/api/webServer/approval,将得到一个包含大量XML格式的内容,实际上他就是上面Web Service定义的WSDL.XML内容中,可以看到服务器API接口中开放的方法
2.客户端调用(此实例为php客户端调用实例,可使用其他语言)
<?php /****************************** * @author sara zhou * create time 2014-06-25 16:00:00 * YII框架API接口端的客户端调用实例 (基于Webservice的接口服务器端) * 注意php.ini开启soap扩展:extension=php_soap.dll ******************************* */ class ClientController extends CController { public function actionGetUserInfo(){ //制定服务器访问地址 $client = new SoapClient('http://localhost/api/webServer/approval'); //调用服务器接口方法 get_user_info()获取用户信息;由于接口是传json数组,故这里以传json格式传递 $res= $client->get_user_info('{"user_ame":"123","request_id":"23123"}'); var_dump($res); } } ?>
3.用户模型层文件 User.php => model
<?php /** * 用户模型 * * @author Sara Zhou */ class User extends CActiveRecord { //Model里要公开的变量,需要用下面的形式标明 /** * @var integer user_id * @soap */ public $id; /** * @var string post title * @soap */ public $title; public static function model($className=__CLASS__) { return parent::model($className); } public function tableName() { return 'user'; } public static function getUserInfo($user_name){ return User::model()->findbyPk($user_name); } }model里要公开的变量,需要用下面的形式标明
/**
* @var integer user_id
* @soap
*/
public $id;
4.请求模型层的日志API文件,ApiLog.php => model
<?php /** * * @author Sara zhou * @create 2014-06-11 * @desc:主要用于记录API访问日志 */ class ApiLog extends CActiveRecord { public static function model($className=__CLASS__) { return parent::model($className); } /** * @return string the associated database table name */ public function tableName() { return 'api_log'; } /* * insert_api_log()记录API请求日志 * @param $require_no * @param $require_param */ public function insert_api_log($require_no,$require_param){ //只做插入 $binsd = array( 'require_no'=> $require_no, 'require_url'=> $_SERVER['HTTP_HOST'] .$_SERVER['REQUEST_URI'], 'require_param'=> $require_param, 'require_time'=> date('Y-m-d H:i:s',$_SERVER['REQUEST_TIME']), 'require_method'=> $_SERVER['REQUEST_METHOD'], 'server_protocol'=> $_SERVER['SERVER_PROTOCOL'], 'create_time'=> date('Y-m-d H:i:s',$_SERVER['REQUEST_TIME']), ); if ($_SERVER["HTTP_X_FORWARDED_FOR"]=="") { $bind['require_ip']=$_SERVER['REMOTE_ADDR'] .':'.$_SERVER['REMOTE_PORT']; } else $bind['require_ip']=$_SERVER["HTTP_X_FORWARDED_FOR"].':'.$_SERVER['REMOTE_PORT']; $this->setAttributes($binsd, false); $this->save(); return $this->attributes['id']; } /* * update_api_log()更新API请求日志 * @param $log_id * @param $response_param * @param $response_time * @param $error_code * @param $error_msg */ public function update_api_log($log_id,$response_param,$response_time,$error_code,$error_msg){ //只做更新 $sql = "UPDATE api_log set response_param=:response_param,response_time=:response_time,error_code=:error_code,error_msg=:error_msg where id=:id"; $command=Yii::app()->db->createCommand($sql); $bind = array( 'id'=> $log_id, 'response_param'=> $response_param, 'response_time'=> $response_time, 'error_code'=> $error_code, 'error_msg'=> $error_msg, ); return $command->execute($bind); } }
5.拦截远程方法调用(Intercepting Remote Method Invocation)
通过实现[IWebServiceProvider]接口,可以拦截所有方法,在[IWebServiceProvider::beforeWebMethod],这个provider可以获取当前CWebService实例,并通过CWebService::methodName来获取当前请求的方法名,它可以返回false,如果这个远程方法因某些原因(比如未经授权的访问)不应被调用
6.注意:
a. 服务器要打开soap功能,在phpinfo里搜soap,如果已经打开了的话有个大标题就是SOAP
b. classMap其实只要是对应的Model就行,比如这里的ApiUser。
c. 代码注释里* @return ApiUser[] the apiUser records ApiUser[]也必须和Model名称一致
d. 代码注释里* @soap 这个也不能少,不然无法通过soap方式访问,想不到注释里还有这么多文章
e. 如果controller里有accessRules的话,得设定访客就可以访问getPredictions()
f. Model里要公开的变量,需要用下面的形式标明:
/**
* @var integer UID of this record
* @soap
*/
public $uid;