💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 4.2.1 模拟的业务场景 在这个开发实战中,我们将模拟实现优酷的开放平台接口,即:[http://open.youku.com/docs](http://open.youku.com/docs) 但这里不涉及具体的内部开发(我们也确实不得而知),而是只从外部的角度,通过PhalApi框架搭建类似的平台接口架构。 ## 4.2.2 优酷URL分析与路由Rewrite 以下是优酷平台的部分接口URL: ```javascript #单条视频基本信息(videos/show_basic) https://openapi.youku.com/v2/videos/show_basic.json #我的详细信息(users/myinfo) https://openapi.youku.com/v2/users/myinfo.json #评论创建(comments/create) https://openapi.youku.com/v2/comments/create.json #搜索节目通过关键词(searches/show/by_keyword) https://openapi.youku.com/v2/searches/show/by_keyword.json ``` 从上面的接口URL,我们可以明显发现一些规律。即: ``` URL = 接口域名 + 版本 + 相对路径.json ``` ###与PhalApi接口规则的冲突 但在PhalApi框架中,我们是通过&service=XXX参数来指定需要的服务的。为此,我们需要在服务端配置一些Rewrite规则以支持这些URL。 简单地,我们可以这样在nginx配置: ```javascript if ( !-f $request_filename ) { rewrite ^/v2/(.*)/(.*).json /v2/?service=$1.$2; } ``` 并为了更兼容PhalApi的风格,我们在入口文件将接收到的service首字母强制为大写,即: ```javascript //$ vim ./Public/v2/index.php if (isset($_REQUEST['service'])) { $_REQUEST['service'] = ucwords($_REQUEST['service']); } ``` 重启一下nginx后,我们可以有浏览器,试着访问: ``` https://openapi.youku.com/v2/videos/show_basic.json ``` 我们将会看到: ```javascript { "ret": 400, "data": [ ], "msg": "非法请求:接口服务Videos.show_basic不存在" } ``` 即表明Rewrite规则已生效,并能正常工作了,哈哈! 以下是更完整的nginx配置: ```javascript server { root /path/to/openapi.youku.com/Public; index index.html index.htm index.php; error_log /var/log/nginx/.error_log; access_log /var/log/nginx/openapi.youku.com.access_log; server_name openapi.youku.com; location / { try_files $uri $uri/ /index.html; } if ( !-f $request_filename ) { rewrite ^/v2/(.*)/(.*).json /v2/?service=$1.$2; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass 127.0.0.1:9000; include fastcgi_params; } } ``` ## 4.2.3 项目的主要目录结构 首先,我们建立了一个Youku的项目目录,并按不同的版本划分了不同的模块,如: ```javascript $ tree ./Youku/ ./Youku/ └── V2 ├── Api │   └── Default.php ├── Domain ├── Model └── Tests ├── Api ├── Domain ├── Model ├── phpunit_user_getbaseinfo.xml ├── phpunit.xml └── test_env.php 8 directories, 4 files ``` 然后,为不同的版本,提供不同的入口,如: ```javascript $ tree ./Public/ ./Public/ ├── checkApiParams.php ├── index.php ├── init.php └── v2 ├── checkApiParams.php ├── index.php └── listAllApis.php 1 directory, 6 files ``` ## 4.2.4 简单的模拟实现 现在,到了接口具体开发的环节,我们将模拟开发 [单条视频基本信息(videos/show_basic)](http://open.youku.com/docs/docs?id=44) 首先,我们可以定义接口: ```javascript //$ vim ./Youku/V2/Api/Videos.php <?php class Api_Videos extends PhalApi_Api { public function getRules() { return array( 'show_basic' => array( 'clientId' => array('name' => 'client_id', 'require' => true, 'desc' => '应用Key'), 'videoId' => array('name' => 'video_id', 'desc' => '视频ID'), 'videoUrl' => array('name' => 'video_url', 'desc' => '视频播放页URL'), ), ); } /** * 单条视频基本信息(videos/show_basic) * * @return string id 视频唯一ID * @return string title 视频标题 * @return string link 视频播放链接 * @return string thumbnail 视频截图 * @return int duration 视频时长,单位:秒 * @return ... ... */ public function show_basic() { } } ``` 然后,使用浏览器在线访问接口文档,访问: ``` http://openapi.youku.com/v2/checkApiParams.php?service=Videos.show_basic ``` 可以看到: ![a pic](http://7qnay5.com1.z0.glb.clouddn.com/20150809.png) 可以看出,这与优酷平台上的接口文档是非常相近的。 最后,我们可以简单模拟实现: ```javascript public function show_basic() { $rs = '{ "id" : "XMjg1MTcyNDQ0", "title" : "泡芙小姐的灯泡 11", "link" : "http://v.youku.com/v_show/id_XMjg1MTcyNDQ0.html", "thumbnail" : "http://g4.ykimg.com/0100641F464E1FC...", "duration" : "910", "category" : "原创", "state" : "normal", "published" : "2011-07-15 09:00:42", "description" : "当一个人在一座城市搬11次家。就意味着准备在这个城市买房了。", "player" : "http://player.youku.com/player.php/sid/XMjg1MTcyNDQ0/v.swf", "public_type" : "all", "copyright_type" : "original", "user" : { "id" : 58921428, "name" : "泡芙小姐", "link" : "http://u.youku.com/user_show/id_UMjM1Njg1NzEy.html" }, "operation_limit": ["COMMENT_DISABLED"], "streamtypes" : ["flv","flvhd","hd"] }'; return json_decode($rs, true); } ``` ## 4.2.5 接口调用效果 虽然是模拟返回(其实是直接强制返回优酷开放平台上的示例数据),但我们还是可以来看下在模拟实现后的接口调用效果。 首先,是缺少client_id时的非法请求: ```javascript #请求 http://openapi.youku.com/v2/videos/show_basic.json #返回 { "ret": 400, "data": [ ], "msg": "非法请求:缺少必要参数client_id" } ``` 然后,尝试一个合法的请求: ```javascript #请求 http://openapi.youku.com/v2/videos/show_basic.json?client_id=test #返回 { "ret": 200, "data": { "id": "XMjg1MTcyNDQ0", "title": "泡芙小姐的灯泡 11", "link": "http://v.youku.com/v_show/id_XMjg1MTcyNDQ0.html", "thumbnail": "http://g4.ykimg.com/0100641F464E1FC...", "duration": "910", "category": "原创", "state": "normal", "published": "2011-07-15 09:00:42", "description": "当一个人在一座城市搬11次家。就意味着准备在这个城市买房了。", "player": "http://player.youku.com/player.php/sid/XMjg1MTcyNDQ0/v.swf", "public_type": "all", "copyright_type": "original", "user": { "id": 58921428, "name": "泡芙小姐", "link": "http://u.youku.com/user_show/id_UMjM1Njg1NzEy.html" }, "operation_limit": [ "COMMENT_DISABLED" ], "streamtypes": [ "flv", "flvhd", "hd" ] }, "msg": "" } ``` 很好,目前运行效果相当流畅。 虽然如此,但我们明显看到了问题所在。 ## 4.2.6 返回格式的自行调整 在上一节中,我们很明显看到了返回格式与优酷现有的不一样,因为PhalApi框架多了一层。 其实,这些调整对于不同的项目来说,都是非常简单。 当项目需要返回的格式有定制化需求时,可以先自实现response服务,再注册。 在此场景,即: ### 先自定义response服务 我们先创建一个公共的目录./Youku/Common,再创建项目需要的特定响应类: ```javascript //$ vim ./Youku/Common/Response.php <?php class Common_Response extends PhalApi_Response_Json { public function getResult() { return $this->data; } } ``` ### 注册response服务 接着,我们对response进行注册: ```javascript //$ vim ./Public/v2/index.php //装载你的接口 DI()->loader->addDirs(array('Youku', 'Youku/V2')); DI()->response = 'Common_Response'; ``` 这里需要稍微注意一下,我们要在装载Youku目录后,才能注册DI()->response。 ### 再次返回 回到刚才那个请求链接,我们可以发现,当再次调用时,会返回: ```javascript { "id": "XMjg1MTcyNDQ0", "title": "泡芙小姐的灯泡 11", "link": "http://v.youku.com/v_show/id_XMjg1MTcyNDQ0.html", "thumbnail": "http://g4.ykimg.com/0100641F464E1FC...", "duration": "910", "category": "原创", "state": "normal", "published": "2011-07-15 09:00:42", "description": "当一个人在一座城市搬11次家。就意味着准备在这个城市买房了。", "player": "http://player.youku.com/player.php/sid/XMjg1MTcyNDQ0/v.swf", "public_type": "all", "copyright_type": "original", "user": { "id": 58921428, "name": "泡芙小姐", "link": "http://u.youku.com/user_show/id_UMjM1Njg1NzEy.html" }, "operation_limit": [ "COMMENT_DISABLED" ], "streamtypes": [ "flv", "flvhd", "hd" ] } ``` ## 4.2.7 签名验证 以上我们调整了返回格式,这使得我们的项目开发,越来越达到优酷开放平台接口的标准了(当然,是假设)。 但有一点,我们是不能忽视的,在很多项目中同样是不能忽视的。那就是:接口的签名验证。 我们可以先来看下优酷开放平台是怎么处理接口签名的。 简单地,优酷会为每一个接入方提供一个client_id,然后在每次接口请求时,通过都需要传递此参数。 为此,我们针对这个client_id编写一个简单的客户端验证服务。如: ```javascript //$ vim ./Youku/Common/ClientCheck.php <?php class Common_ClientCheck implements PhalApi_Filter { public function check() { $clientId = DI()->request->get('client_id'); $allCliendIds = array( 'phalapi', 'oschina' ); if (!in_array($clientId, $allCliendIds)) { throw new PhalApi_Exception_BadRequest('illegal client id'); } } } ``` 然后,在入口处注册一下: ```javascript //$ vim ./Public/v2/index.php DI()->filter = 'Common_ClientCheck'; ``` 当我们,再次打开刚才那个链接: ``` http://openapi.youku.com/v2/videos/show_basic.json?client_id=test ``` 我们会看到空的返回: ```javascript [] ``` 这说明,对客户端的非法请求已拦截成功,但这样用户体验明显不好,因为没有任何的错误提示输出。 为此,我们需要回到刚才自定义的响应类,修改一下: ```javascript //$ vim ./Youku/Common/Response.php <?php class Common_Response extends PhalApi_Response_Json { public function getResult() { if ($this->ret != 200) { return array( 'error' => array( 'code' => $this->ret, 'type' => 'SystemException', 'description' => $this->msg, ), ); } return $this->data; } } ``` 再刷新一下,可以看到和优酷平台接口近似的返回了! ```javascript { "error": { "code": , "type": "SystemException", "description": "非法请求:illegal client id" } } ``` 如需要能返回数据,我们只需要传递正确的client_id(目前是固定的两个)即可: ``` http://openapi.youku.com/v2/videos/show_basic.json?client_id=phalapi ``` ## 4.2.8 尾声 当然,此次的优酷接口模拟开发,我们都只是很简单地表面说明。 这样的目的,不是为了让大家真的去了解优酷接口的内部实现,而是向大家展示,通过PhalApi框架,我们可以更灵活地实现各种业务需求和非功能性的需求。 希望对大家有帮助,夜已深,安。 ### 源代码请访问: ``` https://git.oschina.net/dogstar/PhalApi-Demo-Youku.git ```