4.2.1 模拟的业务场景

在这个开发实战中,我们将模拟实现优酷的开放平台接口,即:http://open.youku.com/docs

但这里不涉及具体的内部开发(我们也确实不得而知),而是只从外部的角度,通过PhalApi框架搭建类似的平台接口架构。

4.2.2 优酷URL分析与路由Rewrite

以下是优酷平台的部分接口URL:

  1. #单条视频基本信息(videos/show_basic)
  2. https://openapi.youku.com/v2/videos/show_basic.json
  3. #我的详细信息(users/myinfo)
  4. https://openapi.youku.com/v2/users/myinfo.json
  5. #评论创建(comments/create)
  6. https://openapi.youku.com/v2/comments/create.json
  7. #搜索节目通过关键词(searches/show/by_keyword)
  8. https://openapi.youku.com/v2/searches/show/by_keyword.json

从上面的接口URL,我们可以明显发现一些规律。即:

  1. URL = 接口域名 + 版本 + 相对路径.json

与PhalApi接口规则的冲突

但在PhalApi框架中,我们是通过&service=XXX参数来指定需要的服务的。为此,我们需要在服务端配置一些Rewrite规则以支持这些URL。简单地,我们可以这样在nginx配置:

  1. if ( !-f $request_filename )
  2. {
  3. rewrite ^/v2/(.*)/(.*).json /v2/?service=$1.$2;
  4. }

并为了更兼容PhalApi的风格,我们在入口文件将接收到的service首字母强制为大写,即:

  1. //$ vim ./Public/v2/index.php
  2. if (isset($_REQUEST['service'])) {
  3. $_REQUEST['service'] = ucwords($_REQUEST['service']);
  4. }

重启一下nginx后,我们可以有浏览器,试着访问:

  1. https://openapi.youku.com/v2/videos/show_basic.json

我们将会看到:

  1. {
  2. "ret": 400,
  3. "data": [
  4. ],
  5. "msg": "非法请求:接口服务Videos.show_basic不存在"
  6. }

即表明Rewrite规则已生效,并能正常工作了,哈哈!

以下是更完整的nginx配置:

  1. server {
  2. root /path/to/openapi.youku.com/Public;
  3. index index.html index.htm index.php;
  4. error_log /var/log/nginx/.error_log;
  5. access_log /var/log/nginx/openapi.youku.com.access_log;
  6. server_name openapi.youku.com;
  7. location / {
  8. try_files $uri $uri/ /index.html;
  9. }
  10. if ( !-f $request_filename )
  11. {
  12. rewrite ^/v2/(.*)/(.*).json /v2/?service=$1.$2;
  13. }
  14. location ~ \.php$ {
  15. fastcgi_split_path_info ^(.+\.php)(/.+)$;
  16. fastcgi_pass 127.0.0.1:9000;
  17. include fastcgi_params;
  18. }
  19. }

4.2.3 项目的主要目录结构

首先,我们建立了一个Youku的项目目录,并按不同的版本划分了不同的模块,如:

  1. $ tree ./Youku/
  2. ./Youku/
  3. └── V2
  4. ├── Api
  5. └── Default.php
  6. ├── Domain
  7. ├── Model
  8. └── Tests
  9. ├── Api
  10. ├── Domain
  11. ├── Model
  12. ├── phpunit_user_getbaseinfo.xml
  13. ├── phpunit.xml
  14. └── test_env.php
  15. 8 directories, 4 files

然后,为不同的版本,提供不同的入口,如:

  1. $ tree ./Public/
  2. ./Public/
  3. ├── checkApiParams.php
  4. ├── index.php
  5. ├── init.php
  6. └── v2
  7. ├── checkApiParams.php
  8. ├── index.php
  9. └── listAllApis.php
  10. 1 directory, 6 files

4.2.4 简单的模拟实现

现在,到了接口具体开发的环节,我们将模拟开发 单条视频基本信息(videos/show_basic)

首先,我们可以定义接口:

  1. //$ vim ./Youku/V2/Api/Videos.php
  2. <?php
  3. class Api_Videos extends PhalApi_Api {
  4. public function getRules() {
  5. return array(
  6. 'show_basic' => array(
  7. 'clientId' => array('name' => 'client_id', 'require' => true, 'desc' => '应用Key'),
  8. 'videoId' => array('name' => 'video_id', 'desc' => '视频ID'),
  9. 'videoUrl' => array('name' => 'video_url', 'desc' => '视频播放页URL'),
  10. ),
  11. );
  12. }
  13. /**
  14. * 单条视频基本信息(videos/show_basic)
  15. *
  16. * @return string id 视频唯一ID
  17. * @return string title 视频标题
  18. * @return string link 视频播放链接
  19. * @return string thumbnail 视频截图
  20. * @return int duration 视频时长,单位:秒
  21. * @return ... ...
  22. */
  23. public function show_basic() {
  24. }
  25. }

然后,使用浏览器在线访问接口文档,访问:

  1. http://openapi.youku.com/v2/checkApiParams.php?service=Videos.show_basic

可以看到:a pic可以看出,这与优酷平台上的接口文档是非常相近的。

最后,我们可以简单模拟实现:

  1. public function show_basic() {
  2. $rs = '{
  3. "id" : "XMjg1MTcyNDQ0",
  4. "title" : "泡芙小姐的灯泡 11",
  5. "link" : "http://v.youku.com/v_show/id_XMjg1MTcyNDQ0.html",
  6. "thumbnail" : "http://g4.ykimg.com/0100641F464E1FC...",
  7. "duration" : "910",
  8. "category" : "原创",
  9. "state" : "normal",
  10. "published" : "2011-07-15 09:00:42",
  11. "description" : "当一个人在一座城市搬11次家。就意味着准备在这个城市买房了。",
  12. "player" : "http://player.youku.com/player.php/sid/XMjg1MTcyNDQ0/v.swf",
  13. "public_type" : "all",
  14. "copyright_type" : "original",
  15. "user" :
  16. {
  17. "id" : 58921428,
  18. "name" : "泡芙小姐",
  19. "link" : "http://u.youku.com/user_show/id_UMjM1Njg1NzEy.html"
  20. },
  21. "operation_limit": ["COMMENT_DISABLED"],
  22. "streamtypes" : ["flv","flvhd","hd"]
  23. }';
  24. return json_decode($rs, true);
  25. }

4.2.5 接口调用效果

虽然是模拟返回(其实是直接强制返回优酷开放平台上的示例数据),但我们还是可以来看下在模拟实现后的接口调用效果。

首先,是缺少client_id时的非法请求:

  1. #请求
  2. http://openapi.youku.com/v2/videos/show_basic.json
  3. #返回
  4. {
  5. "ret": 400,
  6. "data": [
  7. ],
  8. "msg": "非法请求:缺少必要参数client_id"
  9. }

然后,尝试一个合法的请求:

  1. #请求
  2. http://openapi.youku.com/v2/videos/show_basic.json?client_id=test
  3. #返回
  4. {
  5. "ret": 200,
  6. "data": {
  7. "id": "XMjg1MTcyNDQ0",
  8. "title": "泡芙小姐的灯泡 11",
  9. "link": "http://v.youku.com/v_show/id_XMjg1MTcyNDQ0.html",
  10. "thumbnail": "http://g4.ykimg.com/0100641F464E1FC...",
  11. "duration": "910",
  12. "category": "原创",
  13. "state": "normal",
  14. "published": "2011-07-15 09:00:42",
  15. "description": "当一个人在一座城市搬11次家。就意味着准备在这个城市买房了。",
  16. "player": "http://player.youku.com/player.php/sid/XMjg1MTcyNDQ0/v.swf",
  17. "public_type": "all",
  18. "copyright_type": "original",
  19. "user": {
  20. "id": 58921428,
  21. "name": "泡芙小姐",
  22. "link": "http://u.youku.com/user_show/id_UMjM1Njg1NzEy.html"
  23. },
  24. "operation_limit": [
  25. "COMMENT_DISABLED"
  26. ],
  27. "streamtypes": [
  28. "flv",
  29. "flvhd",
  30. "hd"
  31. ]
  32. },
  33. "msg": ""
  34. }

很好,目前运行效果相当流畅。虽然如此,但我们明显看到了问题所在。

4.2.6 返回格式的自行调整

在上一节中,我们很明显看到了返回格式与优酷现有的不一样,因为PhalApi框架多了一层。其实,这些调整对于不同的项目来说,都是非常简单。

当项目需要返回的格式有定制化需求时,可以先自实现response服务,再注册。在此场景,即:

先自定义response服务

我们先创建一个公共的目录./Youku/Common,再创建项目需要的特定响应类:

  1. //$ vim ./Youku/Common/Response.php
  2. <?php
  3. class Common_Response extends PhalApi_Response_Json {
  4. public function getResult() {
  5. return $this->data;
  6. }
  7. }

注册response服务

接着,我们对response进行注册:

  1. //$ vim ./Public/v2/index.php
  2. //装载你的接口
  3. DI()->loader->addDirs(array('Youku', 'Youku/V2'));
  4. DI()->response = 'Common_Response';

这里需要稍微注意一下,我们要在装载Youku目录后,才能注册DI()->response。

再次返回

回到刚才那个请求链接,我们可以发现,当再次调用时,会返回:

  1. {
  2. "id": "XMjg1MTcyNDQ0",
  3. "title": "泡芙小姐的灯泡 11",
  4. "link": "http://v.youku.com/v_show/id_XMjg1MTcyNDQ0.html",
  5. "thumbnail": "http://g4.ykimg.com/0100641F464E1FC...",
  6. "duration": "910",
  7. "category": "原创",
  8. "state": "normal",
  9. "published": "2011-07-15 09:00:42",
  10. "description": "当一个人在一座城市搬11次家。就意味着准备在这个城市买房了。",
  11. "player": "http://player.youku.com/player.php/sid/XMjg1MTcyNDQ0/v.swf",
  12. "public_type": "all",
  13. "copyright_type": "original",
  14. "user": {
  15. "id": 58921428,
  16. "name": "泡芙小姐",
  17. "link": "http://u.youku.com/user_show/id_UMjM1Njg1NzEy.html"
  18. },
  19. "operation_limit": [
  20. "COMMENT_DISABLED"
  21. ],
  22. "streamtypes": [
  23. "flv",
  24. "flvhd",
  25. "hd"
  26. ]
  27. }

4.2.7 签名验证

以上我们调整了返回格式,这使得我们的项目开发,越来越达到优酷开放平台接口的标准了(当然,是假设)。

但有一点,我们是不能忽视的,在很多项目中同样是不能忽视的。那就是:接口的签名验证。

我们可以先来看下优酷开放平台是怎么处理接口签名的。

简单地,优酷会为每一个接入方提供一个client_id,然后在每次接口请求时,通过都需要传递此参数。

为此,我们针对这个client_id编写一个简单的客户端验证服务。如:

  1. //$ vim ./Youku/Common/ClientCheck.php
  2. <?php
  3. class Common_ClientCheck implements PhalApi_Filter {
  4. public function check() {
  5. $clientId = DI()->request->get('client_id');
  6. $allCliendIds = array(
  7. 'phalapi',
  8. 'oschina'
  9. );
  10. if (!in_array($clientId, $allCliendIds)) {
  11. throw new PhalApi_Exception_BadRequest('illegal client id');
  12. }
  13. }
  14. }

然后,在入口处注册一下:

  1. //$ vim ./Public/v2/index.php
  2. DI()->filter = 'Common_ClientCheck';

当我们,再次打开刚才那个链接:

  1. http://openapi.youku.com/v2/videos/show_basic.json?client_id=test

我们会看到空的返回:

  1. []

这说明,对客户端的非法请求已拦截成功,但这样用户体验明显不好,因为没有任何的错误提示输出。

为此,我们需要回到刚才自定义的响应类,修改一下:

  1. //$ vim ./Youku/Common/Response.php
  2. <?php
  3. class Common_Response extends PhalApi_Response_Json {
  4. public function getResult() {
  5. if ($this->ret != 200) {
  6. return array(
  7. 'error' => array(
  8. 'code' => $this->ret,
  9. 'type' => 'SystemException',
  10. 'description' => $this->msg,
  11. ),
  12. );
  13. }
  14. return $this->data;
  15. }
  16. }

再刷新一下,可以看到和优酷平台接口近似的返回了!

  1. {
  2. "error": {
  3. "code": ,
  4. "type": "SystemException",
  5. "description": "非法请求:illegal client id"
  6. }
  7. }

如需要能返回数据,我们只需要传递正确的client_id(目前是固定的两个)即可:

  1. http://openapi.youku.com/v2/videos/show_basic.json?client_id=phalapi

4.2.8 尾声

当然,此次的优酷接口模拟开发,我们都只是很简单地表面说明。

这样的目的,不是为了让大家真的去了解优酷接口的内部实现,而是向大家展示,通过PhalApi框架,我们可以更灵活地实现各种业务需求和非功能性的需求。

希望对大家有帮助,夜已深,安。

源代码请访问:

  1. https://git.oschina.net/dogstar/PhalApi-Demo-Youku.git

原文: https://www.phalapi.net/wikis/4-2.html