在开发中经常会需要写接口给别人调用或者调用别人的接口,通常使用http post发送json格式数据,响应也往往是json格式数据。除了必要的签名,有时候为了数据安全还需要对数据进行对称或非对称加密。这里我们只是模拟post请求发送和响应json格式数据。
1、在library/tools下创建HttpCurl.php工具类
<?php
/**
* @desc curl工具类
* @author zhaoyang
* @date 2018年6月13日 下午8:08:28
*/
namespace Library\Tools;
class HttpCurl {
// 句柄
private $_ch;
// 请求地址
private $_url;
// 是否做https验证
private $_httpsVerify;
// 连接过期时间
private $_timeout;
// 启用时是否将头文件的信息作为数据流输出
private $_header;
private $_option = [ ];
private $_error = null;
/**
* @desc 初始化
* @param string $url 地址
* @param bool $httpsVerify 是否进行https验证
* @param int $timeout 超时时间
* @param bool $header 启用时是否将头文件的信息作为数据流输出
* @author zhaoyang
* @date 2018年6月13日 下午9:00:53
*/
public function __construct(string $url, bool $httpsVerify = false, int $timeout = 30, bool $header = false) {
$this->_url = $url;
$this->_httpsVerify = $httpsVerify;
$this->_timeout = $timeout;
$this->_header = $header;
$this->init();
}
/**
* @desc 基本初始化
* @author zhaoyang
* @date 2018年6月13日 下午8:13:37
*/
private function init() {
$this->_ch = curl_init();
// 让 cURL 自己判断使用哪个版本
$this->_option[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_NONE;
// 在HTTP请求中包含一个"User-Agent: "头的字符串
$this->_option[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT'];
// 需要获取的 URL 地址
$this->_option[CURLOPT_URL] = $this->_url;
// 在尝试连接时等待的秒数。设置为0,则无限等待
$this->_option[CURLOPT_CONNECTTIMEOUT] = $this->_timeout;
// 允许 cURL 函数执行的最长秒数
$this->_option[CURLOPT_TIMEOUT] = $this->_timeout;
// 只获取页面内容,但不输出
$this->_option[CURLOPT_RETURNTRANSFER] = true;
// 发送所支持的编码类型,false发送所有
$this->_option[CURLOPT_ENCODING] = false;
// 启用时会将头文件的信息作为数据流输出
$this->_option[CURLOPT_HEADER] = $this->_header;
// cURL 验证对等证书
$this->_option[CURLOPT_SSL_VERIFYPEER] = $this->_httpsVerify;
// 服务器认证ssl
$this->_option[CURLOPT_SSL_VERIFYHOST] = $this->_httpsVerify;
// 追踪句柄的请求字符串
$this->_option[CURLINFO_HEADER_OUT] = true;
}
/**
* @desc 设置option
* @param string|array $option 选项
* @param mixed $value 值
* @return object $this
* @author zhaoyang
* @date 2018年6月13日 下午8:17:37
*/
public function setOption($option, $value = null) {
if (is_array($option)) {
$this->_option = $option + $this->_option;
} else {
$this->_option[$option] = $value;
}
return $this;
}
/**
* @desc 发送get请求
* @author zhaoyang
* @date 2018年6月13日 下午9:15:16
*/
public function execGet() {
curl_setopt_array($this->_ch, $this->_option);
$result = curl_exec($this->_ch);
$this->_error = [
'errno' => curl_errno($this->_ch),
'error' => curl_error($this->_ch)
];
curl_close($this->_ch);
return $result;
}
/**
* @desc 发送post请求
* @param array|string $requestData 要发送的数据
* @param bool $json 是否发送json格式数据
* @param int $jsonOptions json配置
* @author zhaoyang
* @date 2018年6月13日 下午9:15:33
*/
public function execPost($requestData = null, bool $json = true, int $jsonOptions = null) {
if ($requestData !== null) {
if ($json) {
$isString = is_string($requestData);
(!$isString || ($isString && is_null(json_decode($requestData)))) && $requestData = json_encode($requestData, $jsonOptions);
if (!isset($this->_option[CURLOPT_HTTPHEADER])) {
$this->_option[CURLOPT_HTTPHEADER] = [
'Content-Type: application/json; charset=utf-8',
'Accept: application/json',
'Content-Length:' . strlen($requestData)
];
}
}
$this->_option[CURLOPT_POSTFIELDS] = $requestData;
}
$this->_option[CURLOPT_POST] = true;
curl_setopt_array($this->_ch, $this->_option);
$result = curl_exec($this->_ch);
$this->_error = [
'errno' => curl_errno($this->_ch),
'error' => curl_error($this->_ch)
];
curl_close($this->_ch);
return $result;
}
/**
* @desc 获取错误
* @return array
* @author zhaoyang
* @date 2018年6月13日 下午9:30:23
*/
public function getError() {
return $this->_error;
}
}
2、打开common/BaseController.php,添加
final protected function sendJson($responseData, int $status = 10000, int $jsonOptions = null, int $depth = 512){
$responseData = [
'status' => $status,
'message' => $status == 10000 ? 'ok' : $responseData,
'data' => $status == 10000 ? $responseData : ''
];
return $this->response->setJsonContent($responseData, $jsonOptions, $depth)->send();
}
完成的common/BaseController.php
<?php
/**
* @desc 控制器基类
* @author zhaoyang
* @date 2018年5月8日 下午10:37:37
*/
namespace Common;
use Phalcon\Mvc\Controller;
use Phalcon\Exception;
class BaseController extends Controller {
/**
* @desc 获取get参数
* @param string $name 参数名
* @param string|array $filter 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 当为false时,不使用默认过滤,当为字符串例如'string,trim'时采用参数过滤 ,当为数组例如['string','trim']时采用参数+默认过滤,当为null等其他值时时采用默认过滤
* @param mixed $defaultValue 默认值
* @param bool $noRecursive 不递归过滤
* @return mixed
* @author zhaoyang
* @date 2018年5月8日 下午10:38:50
*/
final protected function get(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false) {
$data = array_merge($this->request->getQuery(), $this->dispatcher->getParams());
unset($data['_url']);
return $this->sanitize($data, $name, $filter, $defaultValue, $noRecursive);
}
/**
* @desc 获取post参数
* @param string $name 参数名
* @param string|array $filter 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 当为false时,不使用默认过滤,当为字符串'string,trim'时采用参数过滤 ,当为数组['string','trim']时采用参数+默认过滤,当为null等其他值时时采用默认过滤
* @param mixed $defaultValue 默认值
* @param bool $noRecursive 不递归过滤
* @param bool $notAllowEmpty 不允许为空
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午8:40:27
*/
final protected function post(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false, bool $notAllowEmpty = false) {
$data = $this->request->getPost();
return $this->sanitize($data, $name, $filter, $defaultValue, $noRecursive);
}
/**
* @desc 获取post或者get参数
* @param string $name 参数名
* @param string|array $filter 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 当为false时,不使用默认过滤,当为字符串例如'string,trim'时采用参数过滤 ,当为数组例如['string','trim']时采用参数+默认过滤,当为null等其他值时时采用默认过滤
* @param mixed $defaultValue 默认值
* @param bool $noRecursive 不递归过滤
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午9:41:49
*/
final protected function request(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false){
if (isset($name) && $name !== '') {
return $this->post($name, $filter, $defaultValue, $noRecursive) ?? $this->get($name, $filter, $defaultValue, $noRecursive);
}
return array_merge($this->post(null, $filter, $defaultValue, $noRecursive), $this->get(null, $filter, $defaultValue, $noRecursive));
}
/**
* @param string $name 参数名
* @param string|array $filter 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 当为false时,不使用默认过滤,当为字符串例如'string,trim'时采用参数过滤 ,当为数组例如['string','trim']时采用参数+默认过滤,当为null等其他值时时采用默认过滤
* @param mixed $defaultValue 默认值
* @param bool $noRecursive 不递归过滤
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午10:43:11
*/
final protected function json(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false){
$data = $this->request->getJsonRawBody(true);
if (!is_array($data)) {
return [ ];
}
return $this->sanitize($data, $name, $filter, $defaultValue, $noRecursive);
}
/**
* @param array $data 数据源
* @param string $name 参数名
* @param string|array $filter 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 当为false时,不使用默认过滤,当为字符串例如'string,trim'时采用参数过滤 ,当为数组例如['string','trim']时采用参数+默认过滤,当为null等其他值时时采用默认过滤
* @param mixed $defaultValue 默认值
* @param bool $noRecursive 不递归过滤
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午8:20:15
*/
final protected function sanitize(array $data, string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false){
$nowFilter = null;
if (is_string($filter) && !empty($filter)) {
$nowFilter = explode(',', $filter);
} else if ($filter !== false) {
$defaultFilter = $this->config->services->filter->default_filter;
$defaultFilter = isset($defaultFilter) ? explode(',', $defaultFilter) : [ ];
if (is_array($filter)) {
$defaultFilter = array_unique(array_merge($filter, $defaultFilter));
}
if (!empty($defaultFilter)) {
$nowFilter = $defaultFilter;
}
}
if (isset($name) && $name !== '') {
if (isset($data[$name]) && $data[$name] !== '') {
$data = $data[$name];
} else {
$data = $defaultValue;
}
}
if (isset($nowFilter)) {
$data = $this->filter->sanitize($data, $nowFilter, $noRecursive);
}
return $data;
}
/**
* @desc 转发到其他动作
* @param array|string $url 'App\Home\Controllers\forward/index/a/aaa?b=bbb' or 'forward/index/a/aaa?b=bbb' or 'index?b=bbb'
* @param array|string $vars 参数 ['a'=>'aaa','b'=>'bbb'] or 'a=aaa&b=bbb'
* @param sring $namespace 命名空间
* @return void
* @author zhaoyang
* @date 2018年5月24日 下午5:11:26
*/
final protected function forward($url, $vars = null, $namespace = null) {
if (is_array($url)) {
$forward = $url;
} else if (is_string($url)) {
$forward = [ ];
$lastbBackslash = strrpos($url, '\\');
if ($lastbBackslash) {
$namespace = substr($url, 0, $lastbBackslash);
}
if (!empty($namespace)) {
$forward['namespace'] = $namespace;
}
$start = $lastbBackslash === false ? 0 : $lastbBackslash + 1;
$rest = substr($url, $start);
$restStrposRes = strpos($rest, '?');
if($rest == '' || $restStrposRes === 0){
throw new Exception('方法不能为空');
}
if($restStrposRes === false){
$capname = $rest;
$paramsString = null;
}else {
list ($capname, $paramsString) = explode('?', $rest, 2);
$capname = trim($capname, '/');
if (empty($capname)) {
throw new Exception('控制器或方法不能为空');
}
}
$capnameArr = explode('/', $capname);
$capnameArrCount = count($capnameArr);
if ($capnameArrCount == 1) {
$forward['action'] = $capnameArr[0];
} else {
$forward['controller'] = $capnameArr[0];
$forward['action'] = $capnameArr[1];
for ($i = 2; $i < $capnameArrCount; $i += 2) {
$forward['params'][$capnameArr[$i]] = $capnameArr[$i + 1] ?? null;
}
}
if ($paramsString !== null) {
parse_str($paramsString, $paramsArr);
$forward['params'] = array_merge($forward['params'] ?? [ ], $paramsArr);
}
} else {
throw new Exception('url只能为字符串或者数组');
}
if (is_string($vars)) {
$vars = trim($vars, '?');
parse_str($vars, $vars);
}
if (is_array($vars)) {
$forward['params'] = array_merge($forward['params'] ?? [ ], $vars);
}
$this->dispatcher->forward($forward);
}
/**
* @desc 成功跳转
* @param string $message 提示信息
* @param string $jumpUrl 跳转地址
* @param bool $redirect 是否使用response->redirect
* @param bool $externalRedirect 是否跳转到外部地址
* @author zhaoyang
* @date 2018年6月9日 下午11:10:10
*/
final protected function success(string $message, string $jumpUrl = null, bool $redirect = false, bool $externalRedirect = false) {
if (is_null($jumpUrl)) {
$this->flashSession->success($message);
echo '<script>history.go(-1);</script>';
return false;
} else if ($redirect || strpos($jumpUrl, '://') !== false) {
$this->flashSession->success($message);
return $this->response->redirect($jumpUrl, $externalRedirect);
} else {
$this->flash->success($message);
return $this->forward($jumpUrl);
}
}
/**
* @desc 失败跳转
* @param string $message 提示信息
* @param string $jumpUrl 跳转地址
* @param bool $redirect 是否使用response->redirect
* @param bool $externalRedirect 是否跳转到外部地址
* @author zhaoyang
* @date 2018年6月10日 上午12:10:16
*/
final protected function error(string $message, string $jumpUrl = null, bool $redirect = false, bool $externalRedirect = false) {
if (is_null($jumpUrl)) {
$this->flashSession->error($message);
echo '<script>history.go(-1);</script>';
return false;
} else if ($redirect || strpos($jumpUrl, '://') !== false) {
$this->flashSession->error($message);
return $this->response->redirect($jumpUrl, $externalRedirect);
} else {
$this->flash->error($message);
return $this->forward($jumpUrl);
}
}
/**
* @desc 响应json数据
* @param string|array $responseData 数据或错误提示
* @param int $status 返回状态
* @param int $jsonOptions json选项
* @param int $depth 处理数组深度
* @return object
* @author zhaoyang
* @date 2018年6月13日 下午10:05:54
*/
final protected function sendJson($responseData, int $status = 10000, int $jsonOptions = null, int $depth = 512){
$responseData = [
'status' => $status,
'message' => $status == 10000 ? 'ok' : $responseData,
'data' => $status == 10000 ? $responseData : ''
];
return $this->response->setJsonContent($responseData, $jsonOptions, $depth)->send();
}
}
3、在home模块下创建HttpCurlController.php
<?php
namespace App\Home\Controllers;
use Common\BaseController;
use Library\Tools\HttpCurl;
class HttpCurlController extends BaseController {
public function indexAction() {
// 简化请求流程,不做签名和加密
/* $requestData = [
'a' => '10a',
'b' => 15
]; */
$requestData = [
'a' => 10,
'b' => 15
];
$url = 'http://phalcon.com/admin/index/test';
$httpCurl = new HttpCurl($url);
$res = $httpCurl->execPost($requestData);
if($res === false){
var_dump($httpCurl->getError());
exit;
}
var_dump($res, json_decode($res, true));
exit;
}
}
4、打开admin模块下IndexController.php控制器
<?php
namespace App\Admin\Controllers;
use Common\BaseController;
use Common\Validate;
class IndexController extends BaseController {
public function indexAction() {
echo __METHOD__, '<br>';
var_dump($this->get());
exit();
}
public function testAction() {
// 忽略校验签名等
$rules = [
['a', 'regex', 'a只能为小于9999的正整数', '/^[1-9]\d{0,3}$/', 1],
['b', 'regex', 'b只能为小于9999的正整数', '/^[1-9]\d{0,3}$/', 1]
];
$data = $this->json();
$validate = new Validate();
$messages = $validate->addRules($rules)->validate($data);
if($messages->count()){
return $this->sendJson($messages->current()->getMessage(), 10001);
}
$sum = $data['a'] + $data['b'];
$responseData = [
'sum' => $sum
];
return $this->sendJson($responseData);
}
}
5、访问/httpcurl/index
这里只是简单的模拟怎么使用,实际开发中还需要做签名甚至数据加密,由于非对称加密耗性能,一般接第三方接口使用的比较多,公司多端通信可以使用对称加密。