有句话说得好,最可怕的事情不是别人比你优秀,而是优秀的人竟然还比你更努力。 --《考拉小巫的留学成长日记》
此篇章主要是讲述接口统一请求的方式,以及提供一个PHP实现的简单客户端。
我们统一固定用service参数来表示需要请求获得的服务,并通过GET方式传递,即请求的URI格式为:
接口域名 + 入口路径 + ?service=XXX.XXX 如: http://dev.phalapi.com + /demo/ + ?service=User.GetBaseInfo
当我们在浏览器以GET方式请求时,可以在nignx看到这样的日记:
127.0.0.1 - - [07/Feb/2015:22:46:46 -0800] "GET /demo/?service=User.GetBaseInfo&sign=&userId=1 HTTP/1.1" 200 107 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:11.0) Gecko/20100101 Firefox/11.0"
如果通过接口用POST方式请求,则会看到:
127.0.0.1 - - [07/Feb/2015:19:32:05 -0800] "POST /demo/?service=User.GetBaseInfo&sign= HTTP/1.1" 200 135 "-" "-"
这里service的名称,开头不区分大小写,建议统一以大写开头,以显得专业。对应的接口是:
class Api_User extends PhalApi_Api { public function getBaseInfo() { } }
在一个项目中,会有很多公共的接口参数,如客户端、版本号、密钥等。这些同样可以纳入GET参数里面,或者也可以放到POST里面。
温馨提示:
这样要求是有目的的,因为这样的话可以在nginx的access日记里面查看来自客户端的快照信息,以便统计或者定位问题。
特别地,接口参数我们建议统一使用POST方式传递,理由很简单:
1、相对保护上传的数据,一如密码;
2、避免特殊字符或者过大数据包在GET下的限制;
默认地,PhalApi框架会将$_REQUEST作为接口参数的来源:
$this->request = 'PhalApi_Request';
当我们需要统一强制用$_GET,可以这样简单定制:
$this->request = new PhalApi_Request($_GET);
同样,也可以强制用$_POST:
$this->request = new PhalApi_Request($_POST);
在测试环境下,为了模拟接口请求,我们需要人工提供接口参数,因此可以这样轻松模拟:
$str = 'service=User.GetBaseInfo&userId=1'; parse_str($str, $params); DI()->request = new PhalApi_Request($params);
先看下调用和使用的代码示例:
<?php require_once './PhalApiClient.php'; $config = array( 'host' => 'http://dev.phalapi.com/demo', 'secrect' => '******' ); $client = new PhalApiClient($config); $rs = $client->request('User.GetBaseInfo', array('userId' => 1)); if ($client->getRet() == PhalApiClient::RET_OK) { var_dump($rs); } else { var_dump($client->getMsg()); var_dump($client->getUrl()); }
附调用接口的客户端源代码:
//$ vim ./PhalApiClient.php <?php class PhalApiClient { protected $host; protected $secrect = ''; protected $params = array(); protected $moreParams = array(); protected $url; protected $ret; protected $msg; protected $data = array(); const RET_OK = 'OK'; const RET_WRONG = 'WRONG'; const RET_ERROR = 'ERROR'; public function __construct($config) { $this->host = rtrim($config['host'], '/') . '/'; $this->secrect = $config['secrect']; } public function request($service, $params = array(), $timeoutMs = 3000) { if (!empty($service)) { $this->params['service'] = $service; } $this->params['sign'] = $this->encryptAppKey($params, $this->secrect); $this->url = $this->host . '?' . http_build_query($this->params); $this->moreParams = $params; $rs = $this->doRequest($this->url, $params, $timeoutMs); if ($rs === false) { $this->ret = self::RET_ERROR; $this->msg = '后台接口请求超时'; return $this->getData(); } $rs = json_decode($rs, true); if (isset($rs['data']['code']) && $rs['data']['code'] != 0) { $this->ret = self::RET_WRONG; $this->msg = '接口调用失败[code =' . $rs['data']['code'] . ']' . ', 错误>信息:' . isset($rs['data']['msg']) ? $rs['data']['msg'] : '无'; return $this->getData(); } $this->ret = intval($rs['ret']) == 200 ? self::RET_OK : self::RET_WRONG; $this->data = $rs['data']; $this->msg = $rs['msg']; return $this->getData(); } public function getRet() { return $this->ret; } public function getData() { return $this->data; } public function getMsg() { return $this->msg; } public function getUrl() { return $this->url . '&' . http_build_query($this->moreParams); } protected function encryptAppKey($params, $secrect) { return ''; } protected function doRequest($url, $data, $timeoutMs = 3000) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $timeoutMs); if (!empty($data)) { curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } $rs = curl_exec($ch); curl_close($ch); return $rs; } }