乐趣区

关于php:API接口请求的配置化封装

抛出问题

  • 问题 1:日常开发中,咱们都会遇到接口类的申请,各种各样的第三方接口,以及外部接口等,大家都是怎么方便快捷的保护这些接口的疾速申请呢?
  • 问题 2:面对第三方的接口,对接的时候记得,后续查找会不会感觉找不到在哪了呢?或者查找归类比拟麻烦呢?
  • 问题 3:相似的第三方接口,你是否写反复的办法调用和处理结果呢?
  • ……

如果你有下面的相似问题,是否会思考优化呢?也就是把几户一样的接口做成配置化,不论是调用还是前期保护都比拟不便呢?本着“不便别人,造福本人”的开发准则,必须从优解决,安顿!

API 接口例子

// 接口 1
地址:$url = 'https://www.test.com/api/test';
参数:$param = array('id' => 1, 'code' => '123', 'status' => 1);
返回:array('code' => 200, 'msg' => '');

// 接口 2
地址:$url = 'https://www.test.com/api/test2';
参数:$param = array('type' => 1, 'num' => '');
返回:array('code' => 200, 'msg' => '');

// 接口 N 

针对下面的两个接口,如果做成配置化的申请,也就是,写好对立的申请入口,对于后续新增同样接口,只须要依据格局配置接口地址以及申请参数即可。

接口申请类设计准则

  1. 繁多入口
  2. 接口信息可配置化
  3. 对立校验接口参数,避免非法参数申请
  4. 对立的申请办法和解决
  5. 接口返回数据格式统一
  6. ……

申请类的繁多入口

繁多入口是指每个接口申请,公共申请这个办法即可,此办法须要传配置的接口键的值,以及申请参数等,具体能够看代码里的正文阐明。

/**
 * 接口申请入口
 * @param string $action 配置的接口键名
 * @param array $param 申请参数
 * @param bool $is_format 是否格式化申请后果
 * @param bool $debug 是否开启调试模式
 * @return array|string 返回申请后果
 */
public static function request($action = '', $param = array(), $is_format = true, $debug = false)
{
    // 初始化
    self::initParam();
    // 校验参数,并设置申请参数
    $check = self::setApiParam($action, $param);
    if (!$check) {return self::$result;}
    // 发动接口申请
    $response = self::curlByPost(self::$url, self::$param, self::$token, self::$type);
    // 无需格式化间接返回
    if (!$is_format) {return $response;}
    // 设置返回值
    self::setResult($response);
    // 是否调试输入申请信息
    if ($debug) {self::echo_msg("url:" . self::$url);
        self::echo_msg("token:" . self::$token);
        self::echo_msg("param:" . json_encode(self::$param));
        self::echo_msg("response:" . $response);
    }
    return self::$result;
}

配置接口信息

下面的 2 个接口,间接做成配置化的模式,此配置 api_config 的键的名字能够自定义,最好命名为接口的阐明,url 为接口地址,type 为 Content-Type 的类型抉择,param 就是接口参数,具体的参数配置看正文即可。

// 接口地址,申请形式,参数的配置
// 1. required:是否必传(true 为必传,false 为选传)// 2. default:可设置默认值,不传值时取
// 3. range:可设置传值范畴,只可传范畴内的值
// 其余可自在批改进行扩大
private static $api_config = array(
    // 测试接口 1 配置
    'test_1' => array(
        'url' => 'https://www.test.com/api/test',
        'type' => 'x-www-form-urlencoded',
        'param' => array('id' => array('required' => true, 'default' => 1),
            'code' => array('required' => true),
            'status' => array('required' => true, 'range' => array(1, 2))
        )
    ),
    // 测试接口 2 配置
    'test_2' => array(
        'url' => 'https://www.test.com/api/test2',
        'type' => 'form-data',
        'param' => array('type' => array('required' => true, 'default' => 1),
            'num' => array('required' => false)
        )
    ),
);

校验参数

依据配置的参数,进行校验申请传入的参数是否无效,能够无效过滤局部非法参数

/**
 * 设置接口申请的参数和地址等申请信息
 * @param string $action 配置的接口键名
 * @param array $param 申请参数
 * @return bool 返回虚实
 */
public static function setApiParam($action = '', $param = array())
{
    // 查看接口办法是否已配置
    if (!isset(self::$api_config[$action])) {self::$result['msg'] = '接口未配置';
        return false;
    }
    // 获取接口配置
    $api_info = self::$api_config[$action];
    $api_param = $api_info['param'];
    // 循环校验参数
    foreach ($api_param as $key => $config) {if (!isset($param[$key])) {
            // 字段未传
            if ($config['required'] && !isset($config['default'])) {
                // 必传,并且没有默认值则报错
                self::$result['msg'] = '参数 [' . $key . '] 必传';
                return false;
            }
            // 非必传,取默认值
            isset($config['default']) && self::$param[$key] = $config['default'];
        } else {
            // 字段已传
            self::$param[$key] = $param[$key];
        }
        // 判断是否在取值范畴内
        if (self::$param[$key] && isset($config['range'])) {if (!in_array(self::$param[$key], $config['range'])) {
                // 不在取值范畴内
                self::$result['msg'] = '参数 [' . $key . '] 的值不在取值范畴内';
                return false;
            }
        }
    }
    // 如果须要获取 token 密钥,此办法须要 to-do,如果其余签名形式可依据理论批改
    self::$token = self::getToken();
    // 接口地址
    if (empty($api_info['url'])) {self::$result['msg'] = '接口地址为空';
        return false;
    }
    self::$url = $api_info['url'];
    // Content-Type 申请类型
    if (!isset(self::$headers[$api_info['type']])) {self::$result['msg'] = '无此申请类型';
        return false;
    }
    self::$type = $api_info['type'];
    return true;
}

残缺源码

下面说的三点为外围局部,可能看起来有点云里雾里的感觉,我必定不是那种不将就的人,残缺的源码必须安顿上,如有写到不到位或者不全的,请不要吐槽我啦,你懂的。

<?php

/**
 * Created by PhpStorm.
 * User: gxcuizy
 * Date: 2021/02/20
 * Time: 下午 13:18
 * api 接口申请封装类
 * Class ApiRequest
 */
class ApiRequest
{
    // 接口地址
    private static $url = '';
    // 申请 header 类型
    private static $type = '';
    // 接口密钥
    private static $token = '';
    // 申请参数
    private static $param = array();
    // 返回数组格局
    private static $result = array(
        'code' => 0,
        'msg' => '','data' => array());
    // header 头类型数组
    private static $headers = array(''=> array(),'form-data'=> array('Content-Type: multipart/form-data'),'x-www-form-urlencoded'=> array('Content-Type:application/x-www-form-urlencoded'),'json'=> array('Content-Type: application/json'),
    );
    // 接口地址,申请形式,参数的配置
    // 1. required:是否必传(true 为必传,false 为选传)// 2. default:可设置默认值,不传值时取
    // 3. range:可设置传值范畴,只可传范畴内的值
    // 其余可自在批改进行扩大
    private static $api_config = array(
        // 测试接口 1 配置
        'test_1' => array(
            'url' => 'https://www.test.com/api/test',
            'type' => 'x-www-form-urlencoded',
            'param' => array('id' => array('required' => true, 'default' => 1),
                'code' => array('required' => true),
                'status' => array('required' => true, 'range' => array(1, 2))
            )
        ),
        // 测试接口 2 配置
        'test_2' => array(
            'url' => 'https://www.test.com/api/test2',
            'type' => 'form-data',
            'param' => array('type' => array('required' => true, 'default' => 1),
                'num' => array('required' => false)
            )
        ),
    );

    /**
     * 接口申请入口
     * @param string $action 配置的接口键名
     * @param array $param 申请参数
     * @param bool $is_format 是否格式化申请后果
     * @param bool $debug 是否开启调试模式
     * @return array|string 返回申请后果
     */
    public static function request($action = '', $param = array(), $is_format = true, $debug = false)
    {
        // 初始化
        self::initParam();
        // 校验参数,并设置申请参数
        $check = self::setApiParam($action, $param);
        if (!$check) {return self::$result;}
        // 发动接口申请
        $response = self::curlByPost(self::$url, self::$param, self::$token, self::$type);
        // 无需格式化间接返回
        if (!$is_format) {return $response;}
        // 设置返回值
        self::setResult($response);
        // 是否调试输入申请信息
        if ($debug) {self::echo_msg("url:" . self::$url);
            self::echo_msg("token:" . self::$token);
            self::echo_msg("param:" . json_encode(self::$param));
            self::echo_msg("response:" . $response);
        }
        return self::$result;
    }

    /**
     * 初始化参数值
     */
    private static function initParam()
    {
        self::$url = '';
        self::$type = '';
        self::$token = '';
        self::$param = array();
        self::$result['code'] = 0;
        self::$result['msg'] = '';
        self::$result['data'] = array();}

    /**
     * 设置接口申请的参数和地址等申请信息
     * @param string $action 配置的接口键名
     * @param array $param 申请参数
     * @return bool 返回虚实
     */
    public static function setApiParam($action = '', $param = array())
    {
        // 查看接口办法是否已配置
        if (!isset(self::$api_config[$action])) {self::$result['msg'] = '接口未配置';
            return false;
        }
        // 获取接口配置
        $api_info = self::$api_config[$action];
        $api_param = $api_info['param'];
        // 循环校验参数
        foreach ($api_param as $key => $config) {if (!isset($param[$key])) {
                // 字段未传
                if ($config['required'] && !isset($config['default'])) {
                    // 必传,并且没有默认值则报错
                    self::$result['msg'] = '参数 [' . $key . '] 必传';
                    return false;
                }
                // 非必传,取默认值
                isset($config['default']) && self::$param[$key] = $config['default'];
            } else {
                // 字段已传
                self::$param[$key] = $param[$key];
            }
            // 判断是否在取值范畴内
            if (self::$param[$key] && isset($config['range'])) {if (!in_array(self::$param[$key], $config['range'])) {
                    // 不在取值范畴内
                    self::$result['msg'] = '参数 [' . $key . '] 的值不在取值范畴内';
                    return false;
                }
            }
        }
        // 如果须要获取 token 密钥,此办法须要 to-do,如果其余签名形式可依据理论批改
        self::$token = self::getToken();
        // 接口地址
        if (empty($api_info['url'])) {self::$result['msg'] = '接口地址为空';
            return false;
        }
        self::$url = $api_info['url'];
        // Content-Type 申请类型
        if (!isset(self::$headers[$api_info['type']])) {self::$result['msg'] = '无此申请类型';
            return false;
        }
        self::$type = $api_info['type'];
        return true;
    }

    /**
     * POST 申请
     * @param string $url 接口地址
     * @param array $data 申请参数
     * @param string $token 密钥 token
     * @param string $type 传参类型
     * @param array $header_ext 扩大的 header 信息
     * @return bool|string 返回申请后果
     */
    private static function curlByPost($url = '', $data = array(), $token ='', $type = 'json', $header_ext = array())
    {$header = self::$headers[$type];
        // 是否须要 token
        if ($token) {$header[] = "Authorization:$token";
        }
        // 扩大的 header 信息
        if (!empty($header_ext)) {$header = array_merge($header, $header_ext);
        }
        // 发送 POST 申请
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $output = curl_exec($ch);
        curl_close($ch);
        return $output;
    }

    /**
     * 解决接口响应返回
     * @param string $response 接口返回数据
     */
    private static function setResult($response = '')
    {if ($response) {
            // 有返回值
            $result = json_decode($response, true);
            if (isset($result['code']) && $result['code'] == 200) {
                // 申请胜利
                self::$result['data'] = $result['data'];
                self::$result['code'] = 200;
            } else {
                // 申请失败
                if (isset($result['code'])) {self::$result['code'] = $result['code'];
                }
                self::$result['msg'] = $response;
            }
        } else {
            // 无返回值异样
            self::$result['msg'] = "empty response.";
        }
    }

    /**
     * 获取密钥 token(TODO)* @return string
     */
    private static function getToken()
    {
        $token = '';
        return $token;
    }

    /**
     * 打印输出信息
     * @param string $msg 输入文本
     */
    private static function echo_msg($msg = '')
    {if (!empty($msg)) {$msg = "[" . date("Y-m-d H:i:s") . "]" . $msg . PHP_EOL;
            echo $msg;
            @ob_flush();
            @flush();}
    }
}

最初

刚过完春节,大家预计都还没缓过劲来进入 Coding 状态,恭祝大家在新的一年里可能播种多多,播种满满,2021 所想皆能实现。如果大家有什么好的想法或者倡议,也能够底部留言给我哈,感激哦!

退出移动版