(二) 创建符合规范的 API 接口- Build API For Your Company 系列
本文内容
完成 测试数据填充 的工作,我们就需要筹划一下API究竟怎么写的事情了?或许你的项目经理会给你一份API文档,让你实现就可以。这样你就不用来做本部分的工作。更多的时候你可能也是这些接口的制订者,你需要和你的团队伙伴一起讨论并确定一个可行的API接口,由你来规范并整理出文档,或者整个项目都是你一个人负责,当然你也需要和自己聊聊改如何来处理这个阶段的事情:计划并创建符合规范的接口
。
我们用 Endpoint 这个词来代指我们需要处理的对象:单个的接口,
就像 GET https://api.example.com/foo/bar
处理业务需求的阶段,这么说吧,我们目前接触到得很多开发任务可能都会涉及到一系列 Resource
的 CRUD(Create, Read, Update, Delete)的操作, 如果你对 resource 这个概念还不是很了解,可能需要补充一点概念知识:
- Web Resource Wiki: https://en.wikipedia.org/wiki/Web_resource
- A Short History of "Resource" in web architecture. https://www.w3.org/DesignIssues/TermResour...
简单解释一下,Resource
可以理解为我们在应用中抽象出来的一种对象实体,比如你要开发一个基于Map的应用,你所处理的一系列地点(Places)就可以作为程序中的一种资源。你所做的是去处理一下这中资源,为其创建对应的APIs,来完成对该资源的操作。
废话不多说,我们可以实际开发中的操作。TDD
(Test Driven Development)的开发流程确实是一种非常不错的开发方式,个人觉得API的开发更应该采用这种方式。我们也将会用这种方式来完成本系列的讲解。思维回到之前解释 Resource 时使用到的一个示例,假设我们有这样一些商业需求:
- 在地图(Map)上有多个地点;
- 每个地点(Place)的一定范围内有多个商业机构;
- 每个商业机构(Merchant)可以提供一些商业服务(Service);
- 用户(User)可以在地图上点击商业机构,来完成一些需求(登记、购买、预约等)
所以你的脑海中需要一个 "Places" 资源的概念存在,快速列出这个资源需要处理的 Actions 列表:
Places
- Create
- Read
- Update
- Delete
显而易见,我们需要能够查看这些 Places,需要能够创建、更新和删除这些资源。考虑的更多一点,我们有时候需要列表展示符合一定条件的 Places 列表(分页的内容后面会谈及), 修改一下:
Places
- Create
- Read
- Update
- Delete
- List
如果想让这个列表更有效的辅助开发和团队交流,你可以给这个列表加上一些简单的内容:
...
- List(Lat, lon, distance, name)
我们并没有在 Create 和 Update 这两个操作上面添加更多信息,是应该这两个接口应该是牵扯业务逻辑最多的接口,我们现阶段的目标只是列出需要创建出一个接口列表,没有必要去思考多么详尽的接口设计,时候未到。
考虑的再多一点,地点这个资源可能会有需要展示的图片,那就需要用户上传图片到服务器。当然,完全可以把图片上传的实现设计到 Update 操作里面去,但是上传文件可能需要我们考虑的东西更多一点,你可能需要设计如何去保存到本地,或者上传到其他云服务提供商,需要对文件的验证和进度有个把控,这些东西其实还是蛮烦人的。所以更建议把文件上传这个接口给分离出来更好一点。我们创建一个Image操作来处理上传图片的问题:
Places
- Create
- Read
- Update
- Delete
- List(Lat, lon, distance, name)
- Image
根据这个思路,我们可以为这个简单的商业应用,列出一个简单、完整的 API Actions 列表:
Places
- Create
- Read
- Update
- Delete
- List(Lat, lon, distance, name)
- Image
Merchants
- Create
- Read
- Update
- Delete
- List(Lat, lon, name, address, telephone)
Services
- Create
- Read
- Update
- Delete
- List
Users
- Create
- Create
- Read
- Update
- Delete
- List(active)
- Image
- Services
- Favorites
有了这个列表,我们其实就可以着手进行代码工作了,但是在把这些操作转换成我们熟悉的一个个 Endpoint
之前,我们还是需要了解一些理论知识:
Endpoint Theory
简单来说,就是需要你对 RESTful
这个概念有一定的了解,我简单整理了一些资料:
-
RESTful Wiki:
中文Wiki:https://baike.baidu.com/view/5798116.htm?f...
Wikipedia:https://zh.wikipedia.org/wiki/REST
-
Some Blogs:
-
Some Books:
如果你对REST开发很感兴趣而且有空闲时间,可以去读一下下面的这些资料:
Roy Thomas Fielding
的经典论文:Architectural Styles and the Design of Network-based Software Architectures(English)
HATEOAS
概念: REST APIs must be hypertext-driven不错的两本书(版权问题,在这里不共享链接):
RESTful Web Services
REST in Pratice(中英文在亚马逊都有卖,我正在拜读,强烈推荐)
Best Practices
的命名习惯(注:这部分内容翻译本系列参考书籍的部分章节)
-
Get Resources
GET /resources
返回一个分页的列表或者是默认指定的资源信息GET /resources/X
只返回一个指定的资源信息,X可以是ID, hash值,slug,username,等等,只要这个标识对于资源来说是唯一的(unique)GET /resources/X,Y,Z
客户端想同时获取多个资源信息,服务器返回包含多个资源信息的集合
-
Embeding Data
需要嵌套资源返回的详细我们之后的系列会聊到,在这里可以简单的看一下。其实我们经常做这种资源嵌套(nested resources or embedding resource), 当我们想要获取到某个地点的所有商业就列表的时候,你的
EndPoint
应该是这样的格式:GET /places/X/merchants
找到所有该地点范围内的商人资源列表GET /users/X/services
返回用户所参与的所有服务(可以通过参数指定状态)列表GET /users/X/services/Y
返回用户的一个特定的服务信息
最后一个示例可能是有争议的,有些人可能更喜欢更简短一点的写法
/services/Y
; -
Increment Id vs Uuid
以前自己写程序的时候经常会将资源
id
这个字段设置为 increments,因为很方便,很多人也建议这么做,但是从商业和安全的角度来看,你可能不希望将你的敏感资源的id
给暴露出去,你不想有人通过遍历 id 来获取一下信息,更不想让你的竞争对手看到这些资源的统计数据。所以使用UUID
或许是一个可以考虑的方式,当然如果有更好的方式,可以在下方留言,咱们可以讨论一下。
在 Github 上有很多来关于Laravel
框架生成uuid
的项目,如果你需要考虑这方面的问题,可以去搜索一下,下面是一个示例:安装(切换到项目目录):
$ composer require webpatser/laravel-uuid dev-master
使用(很简单的):
# Edit `app/config/app.php` and add the `alias` 'aliases' => array( 'Uuid' => 'Webpatser\Uuid\Uuid',) # 基本的用法 Uuid::generate()
-
DELETE Resource
DELETE /places/X
删除单个的地点资源DELETE /places/X,Y,Z
批处理方式删除多个资源DELETE /places
这个操作会删除所有地点资源DELETE /places/X/image
删除指定地点的图片DELETE /places/X/images
如果选择多个图片资源的话,这个操作可以用来实现批量删除的操作
-
POST vs PUT
资源的创建和更新是需要谨慎对待的两个操作。许多人会建议将 HTTP verb(即HTTP方法)匹配到经常处理的CRUD操作,例如:使用 POST 来新建资源信息, PUT 来更新资源信息。但是这种做法常常并不高效和满足不了功能需求;
通常来讲, PUT 方法适用的情况是你处理改操作之前知道整个URL,而且重复的进行该操作(多次请求)不会对造成不同的结果。例如, 当你为一个地点上传一张图片的时候,PUT 方法可能是不错的选择:
PUT /places/1/image HTTP/1.1 Host: example.com Content-Type: image/jpeg
但是当你有多张图片要上传的时候,你就无法确定整个URL,而且多次尝试该操作会导致不同的结果产生,使用POST方法或许更好一点。
更新用户配置
是经常需要处理的一个事情,你可以这样:POST /me/settings
更新单个指定的配置信息PUT /me/settings
更新全部配置信息
这个部分确实比较多变,但是最好不要去把 HTTP method 绑定到指定的 CRUD 操作,毕竟创建 Web 应用,需要很大的灵活性。
-
个人喜好的问题
有些人可能喜欢使用单数形式来命名资源名称, 例如:
/user/X
之类的,但复数形式呈现给其他人的逻辑可能更好一点。当你使用/user
这样的接口时,开发者对接口的返回值会有一个下意识的反应
:这个返回单个用户信息?或许是返回me
的信息等等。这时候你返回整个用户列表就会造成不必要的困惑。或许你会说,返回多个数据的时候还是可以采用/users
这样类似的命名习惯的, 但是采用复数形式的命名习惯可以保持命名的一致性,何乐而不为呢?
Controllers and Routes
有了之前的一些理论知识,用 laravel
快速实现这些接口,首先你需要将这些接口进行一下逻辑的的分组,创建需要的 controller,每种资源对应一个控制器也是一种方案:
- PlacesController
- MerchantsController
- ServicesController
- UsersController
你可以在 laravel 手动创建这些文件,laravel 提供了非常棒的语法来快速实现这些路由:
切换到项目目录下面:
php artisan controller:make UsersController
修改 app\routes.php
文件和 app\controller\UsersController.php
文件,你可以得到下面的这张表:
Action | Endpoint | Route Name | Controller |
---|---|---|---|
Create | POST /users | users.store | UsersController@store |
Read | GET /users/X | users.show | UsersController@show |
Update | POST /users/X | users.update | UsersController@update |
Delete | DELETE /users/X | users.delete | UsersController@destroy |
List(active) | GET /users | users.list | UsersController@list |
Image | PUT /users/X/image | users.image | UsersController@uploadImage |
Services | GET /users/X/services | users.services | UsersController@services |
Favorites | GET /users/X/favorites | users.favorites | UsersController@favorites |
有了这个 Users
的路由示例,其他的控制器的也可以快速的写出来,在这里就不再赘述了。
下篇文章内容
当然,我们现在有了一份经过思考和讨论的草图,但是作为新手,我们可能还需要学习一些 HTTP Requests
和 Responses
的知识,这个就是下个章节介绍的内容。
三更灯火五更鸡,正是男儿发奋时。:+1:
@JobsLong 背景不好, 迷彩服 + 绿叶背景, 都看不到人的轮廓了. :sunglasses:
呦吼呦吼呦吼. 说好的更新呢 :sunglasses:
已更新.
@JobsLong 解析器出现的问题, 请见这里 http://phphub.org/topics/123 , 已经放在我的 Todo list 里面了.
@JobsLong Table 解析 Fixed 了. https://github.com/summerblue/phphub/commi...
晓文大神啊啊啊啊啊啊:+1:
文章最后一处拼写错误
HTPP