API 接口在各种场景中已经非常普遍使用,通常在 PHP 后台调用 API 接口,需要通过 Curl 库来自己封装,且不说各种充值门槛,还要被各种 api 接口平台的 appKey 、appSecret 之类的参数困惑,没法实现统一调用,很多小白更是被 Curl 各种配置参数弄得头大。ThinkPHP官方出品的ThinkAPI服务正是为了解决 PHP 接口调用的各种麻烦问题。
ThinkAPI统一API接口服务是由官方联合合作伙伴封装的一套接口调用服务及 SDK,旨在帮助ThinkPHP开发者更方便和更低成本调用官方及第三方的提供的各类API接口及服务,从而更好的构建开发者生态。
通过ThinkAPI提供的 SDK 功能可以以更优雅的方式来调用 API 接口,首先需要在你的项目里面安装 think-api 库(适用于任何 PHP5.6+项目,没有任何框架要求)。
composer require topthink/think-api
然后就可以调用你需要的接口进行查询和返回数据,支持ThinkAPI所有的 API 接口,以查询身份证所属地区接口为例:
use think\api\Client;
$client = new Client("appCode");
$result = $client->idcardIndex()
->withCardno('身份证号码')
->request();
idcardIndex方法就是调用了身份证归属地查询接口 withCardno方法则表示传入了cardno参数,如果还需要传入更多的参数则链式调用更多的方法即可,最后通过request方法进行实际调用并返回数据。通过 IDE 配合的话,你不需要自己记住任何接口方法名和参数方法名,都会有自动提示。
ThinkAPI所有的 API 调用服务必须设置appCode值(只需要注册一个账号即可获取),用于接口调用的身份认证。如需多次调用的话,建议自己在项目里面封装一个助手函数,例如:
use think\api\Client;
/**
* API 接口调用助手函数
* @return Client
*/
function api(): Client
{
return new Client('yourAppCode');
}
// 调用示例
$result = api()->idcardIndex()
->withCardno('身份证号码')
->request();
所有的接口服务和方法都支持 IDE 自动提示和完成(请务必注意方法大小写必须保持一致),所有的返回数据都是 JSON 格式,因此基本上不需要文档即可完成接口开发工作。API 接口调用中的一些常见问题通过系统的方法封装都可以规避掉,你甚至不需要关心接口是要用 GET 还是 POST,都是系统自动处理的。
SDK 把所有接口和参数都封装为一个个独立的方法,你可以像调用一个类的方法一样简单的调用官方支持的任何 API 接口,也无需再去记住每个接口的参数有哪些。
如果你的环境不支持 Composer 或者 PHP 版本过低,可能需要你自己封装 Curl 库来调用接口。ThinkAPI 接口文档都提供了两种方式调用:直接调用接口地址和使用 SDK 调用。
目前 ThinkAPI 已经接入包括实名认证、人工智能、电子商务、新闻资讯和生活服务等类目在内的常用 API 接口共 269 个,包含大量免费接口,付费接口的价格比较实在、门槛也低,并且还在陆续扩充中。更详细的用法可以参考: https://docs.topthink.com/think-api
1
ben1024 2021-01-20 18:04:08 +08:00
推荐的是付费接口,没有免费接口服务列表吗
|
2
topthink OP @ben1024 https://docs.topthink.com/think-api/1888715 这里有 标注为免费的就是免费接口
|
3
lepig 2021-01-20 21:40:14 +08:00
这个对开发来说确实听友好的。但是奈何老板不同意。
老板说,用付费的金接口,我要你们干啥。 |
4
topthink OP @lepig 现代化开发理念不是什么都要自己独立开发的。每个接口做自己专业的事情 因为专注才能更专业 就和组件化开发一样 接口化也是一种趋势。而且大多数接口自己没有数据没法开发的,比如身份证实名认证 只能调用第三方的。那么 ThinkAPI 的优势就是帮你简化开发 同时也能降低成本。我认为老板关注的不是用不用接口的问题 是项目什么时候能上线的问题吧~
|
6
imgbed 2021-01-20 22:18:02 +08:00
这种付费接口很多商家在做
|
7
lepig 2021-01-20 22:38:06 +08:00
@Actrace 我还真不是。只是我们 cto 为了节约成本新申请了个阿里云账号。然后迁移所有的服务器 /oss/容器等等。然后基础运维部断断续续肝了一个多月才完成。java,php 的所有项目全部要更改配置。
坑的一笔。 > 我是 php 开发。 |
8
justseemore 2021-01-21 13:17:16 +08:00
|
9
topthink OP |
12
unicloud 2021-01-21 16:22:08 +08:00
看标题,以为是技术讨论。
|
13
justseemore 2021-01-21 16:38:09 +08:00
@topthink
额,你不就是一个各种 3 方 api 的代理么。无非就是写了一个更好用的 phpsdk 。。 |
14
ztxcccc 2021-01-21 17:04:55 +08:00
标题和内容有什么关系吗?
|
15
dvaknheo 2021-01-21 18:02:36 +08:00
@topthink 提供优雅的 SDK
什么叫优雅: 老大:想查身份证地区编码,你们这么调就够了: ``` /** @var string */ $area = IdCardService::G()->idcardIndex('身份证号'); ``` 小弟:收到. 几天后, 小弟:报告老大 ,我们身份证地区编码得不到了 老大:收到。现在改 老大:打开入口代码 ``` // IdCardService::G(AliIdService::G()); IdCardService::G(TencentIdService::G()); ``` 老大:好了,已经由接入 A 家改为接入 T 家了。 出错的时候跟踪方便。切换服务提供商方便。小弟们不需要乱动,这才叫优雅。 |
16
dvaknheo 2021-01-21 18:10:33 +08:00
再来个优雅的:
$client = new Client("YourAppCode"); $result = $client->gstoreDisease() ->withSymptom('发烧') ->request(); 老板: 免费接口,每日 100 次免费调用,会员可不限次数调用。 我们接口要烧那么多钱么?减少调用! 老大:没问题。 ``` class MyService{ function gstoreDisease($Symptom ){ // 如果我们数据库已经有,返回数据库已经存好的 // 如果数据库没有,则请求第三方并存入数据 }} 小弟代码不变 // ... $result = MyService::G()->gstoreDisease('发烧'); ``` |
17
dvaknheo 2021-01-21 21:00:33 +08:00
立一个 flag,我重写一个客户端,这三天内完成
|
19
dvaknheo 2021-01-22 00:13:20 +08:00
写好了,一百五十多,放这里可能太长,又没排版 就是实现上述代码的第三方 think-api 接入类。
有人感兴趣的话我就贴在这 |
20
topthink OP |
21
dvaknheo 2021-01-22 16:36:07 +08:00
那我贴出来吧。为什么要有 facade 等东西,目的就是为了实现 “调用方式不变,实现方式可变”啊。
``` <?php require_once(__DIR__.'/vendor/autoload.php'); //@DUCKPHP_HEADFILE use think\api\Client; use GuzzleHttp\Client as GuzzleHttp_Client; use GuzzleHttp\HandlerStack ; use think\helper\Str; trait SingletonExTrait { protected static $_instances = []; public static function G($object = null) { if (defined('__SINGLETONEX_REPALACER')) { $callback = __SINGLETONEX_REPALACER; return ($callback)(static::class, $object); } //fwrite(STDOUT,"SINGLETON ". static::class ."\n"); if ($object) { self::$_instances[static::class] = $object; return $object; } $me = self::$_instances[static::class] ?? null; if (null === $me) { $me = new static(); self::$_instances[static::class] = $me; } return $me; } } class MyService { use SingletonExTrait; public $options =[ 'endpoint' => 'https://api.topthink.com/', 'app_code' => '???', 'default_http_method'=>'GET', ]; public function init(array $options, object $context = null) { $this->options = array_intersect_key(array_replace_recursive($this->options, $options) ?? [], $this->options); } protected function do_call($method, $args) { $http_method = $this->get_http_method($method); $uri = $this->get_uri($method); $parameters = $this->get_parameters($method, $args); try { return $this->do_request($this->options['endpoint'], $this->options['app_code'], $http_method, $uri, $parameters); } catch (RequestException $e) { if ($e->hasResponse()) { $response = $e->getResponse(); throw new Exception($response->getStatusCode(), $response->getBody()->getContents()); } throw $e; } } protected function get_http_method($method) { return $this->options['default_http_method']; } protected function get_uri($method) { //TODO 移除 Str 的引用 return $this->uri_map[$method] ?? Str::snake(class_basename($method), "/"); } protected function get_parameters($method, $args) { $reflect = new \ReflectionMethod($this, $method); $ret=[]; $params = $reflect->getParameters(); foreach ($args as $i => $v) { if($v === null){ continue; } $name = $params[$i]->getName(); $ret[$name] = $v; } return $ret; } protected function do_request($endpoint, $app_code, $method, $uri, $parameters) { $body =[]; if ($method == 'GET') { $options['query'] = $data; } else { $options['body'] = $data; } $handleStack = HandlerStack::create(null); $client = new GuzzleHttp_Client([ 'base_uri' => $endpoint, 'handler' => $handleStack, 'headers' => [ 'Authorization' => "AppCode ".$app_code, 'User-Agent' => "ThinkApi/1.0", ], 'verify' => false, ]); $response = $client->request($method, $uri, $options); $result = $response->getBody()->getContents(); if (false !== strpos($response->getHeaderLine('Content-Type'), 'application/json')) { $result = json_decode($result, true); } return $result; } } // 这个类用脚本生成,省略更多 class CalendarService extends MyService { /** * 查日历 * * @access public * @param mixed $year_month */ public function calendarMonth($yearMonth = null){ return $this->do_call(__FUNCTION__, func_get_args()); } } class LocalCalendarService extends CalendarService { public function calendarMonth($yearMonth = null){ return ['这是模拟数据']; } } //* // 这里是核心工程师老大的干活 //CalendarService::G(LocalCalendarService::G()); // 线上出问题的时候切这句 $options=['app_code'=>'???']; CalendarService::G()->init($options); //// 下面是小弟写 $result = CalendarService::G()->calendarMonth('2019-1'); var_dump($result); return; //*/ $client = new Client('???'); $result = $client->calendarMonth()->withYearMonth('2019-1')->request(); var_dump($result); ``` |
22
topthink OP @dvaknheo 你这还是基于 think-api 的库 有什么优势 我还以为你要用 100 行代码写一个 think-api 出来呢,其实 think-api 库就是一个 IDE 提示工具而已 真正的调用又不是在这个库 你把问题想的太简单了吧
|
23
dvaknheo 2021-01-22 18:54:21 +08:00
@topthink 没基于 think-api 啊, 除了用字符串处理部分还用到 think\helper\Str 而已。 最前面的 use think\api\Client 是后面代码切换演示方便而已。
|
24
topthink OP @dvaknheo 问题是我看不明白你如何离开了 think-api 或者切换到其它 sdk 的支持 怎么做到同样的优雅调用? think-api 的优雅的前提是统一封装了 api 接口的 这个才是最大的问题和工作量
|
25
dvaknheo 2021-01-23 18:45:18 +08:00
@topthink
这个客户端,优雅在这里: 小弟不用改业务代码,只要老大在核心代码那里修改到子类就可以切换实现 CalendarService 是根据 think-api 的库生成的。CalendarService 的所有方法都会在 IDE 里实现。没必要方法和文档分离。 所有的 API 函数方法 内容都一句 return $this->do_call(__FUNCTION__, func_get_args()); CalendarService::G(LocalCalendarService::G()); 演示的是切到第三方的实现 。 这里只是简单的输出 ['这是模拟数据'] 。 实际应用的时候,可以针对性的各种修改,比如 api 结果如果有缓存则用缓存。 比如当对 think-api 的返回的结果异常 如接口上线, 可以添加其他 服务端的支持。 这一切,不需要改动业务代码。 |