(二) 创建符合规范的 API 接口- Build API For Your Company 系列

本文内容

完成 测试数据填充 的工作,我们就需要筹划一下API究竟怎么写的事情了?或许你的项目经理会给你一份API文档,让你实现就可以。这样你就不用来做本部分的工作。更多的时候你可能也是这些接口的制订者,你需要和你的团队伙伴一起讨论并确定一个可行的API接口,由你来规范并整理出文档,或者整个项目都是你一个人负责,当然你也需要和自己聊聊改如何来处理这个阶段的事情:计划并创建符合规范的接口

我们用 Endpoint 这个词来代指我们需要处理的对象:单个的接口,
就像 GET https://api.example.com/foo/bar

处理业务需求的阶段,这么说吧,我们目前接触到得很多开发任务可能都会涉及到一系列 Resource 的 CRUD(Create, Read, Update, Delete)的操作, 如果你对 resource 这个概念还不是很了解,可能需要补充一点概念知识:

简单解释一下,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 这个概念有一定的了解,我简单整理了一些资料:


Best Practices 的命名习惯(注:这部分内容翻译本系列参考书籍的部分章节)
  1. Get Resources

    • GET /resources 返回一个分页的列表或者是默认指定的资源信息
    • GET /resources/X 只返回一个指定的资源信息,X可以是ID, hash值,slug,username,等等,只要这个标识对于资源来说是唯一的(unique)
    • GET /resources/X,Y,Z 客户端想同时获取多个资源信息,服务器返回包含多个资源信息的集合
  2. Embeding Data

    需要嵌套资源返回的详细我们之后的系列会聊到,在这里可以简单的看一下。其实我们经常做这种资源嵌套(nested resources or embedding resource), 当我们想要获取到某个地点的所有商业就列表的时候,你的 EndPoint 应该是这样的格式:

    • GET /places/X/merchants 找到所有该地点范围内的商人资源列表
    • GET /users/X/services 返回用户所参与的所有服务(可以通过参数指定状态)列表
    • GET /users/X/services/Y 返回用户的一个特定的服务信息

    最后一个示例可能是有争议的,有些人可能更喜欢更简短一点的写法 /services/Y

  3. Increment Id vs Uuid

    以前自己写程序的时候经常会将资源 id 这个字段设置为 increments,因为很方便,很多人也建议这么做,但是从商业和安全的角度来看,你可能不希望将你的敏感资源的 id 给暴露出去,你不想有人通过遍历 id 来获取一下信息,更不想让你的竞争对手看到这些资源的统计数据。所以使用 UUID 或许是一个可以考虑的方式,当然如果有更好的方式,可以在下方留言,咱们可以讨论一下。
    在 Github 上有很多来关于 Laravel 框架生成 uuid 的项目,如果你需要考虑这方面的问题,可以去搜索一下,下面是一个示例:

    Laravel-uuid Github
    Packagist

    安装(切换到项目目录):

    $ composer require webpatser/laravel-uuid dev-master

    使用(很简单的):

    # Edit `app/config/app.php` and add the `alias`
    'aliases' => array(
    'Uuid' => 'Webpatser\Uuid\Uuid',)
    
    # 基本的用法
    Uuid::generate()
  4. DELETE Resource

    • DELETE /places/X 删除单个的地点资源
    • DELETE /places/X,Y,Z 批处理方式删除多个资源
    • DELETE /places 这个操作会删除所有地点资源
    • DELETE /places/X/image 删除指定地点的图片
    • DELETE /places/X/images 如果选择多个图片资源的话,这个操作可以用来实现批量删除的操作
  5. 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 应用,需要很大的灵活性。

  6. 个人喜好的问题

    有些人可能喜欢使用单数形式来命名资源名称, 例如: /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 RequestsResponses 的知识,这个就是下个章节介绍的内容。

Remote. Open. Engineer.
本帖已被设为精华帖!
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 8
Summer

三更灯火五更鸡,正是男儿发奋时。:+1:

9年前 评论
Summer

@JobsLong 背景不好, 迷彩服 + 绿叶背景, 都看不到人的轮廓了. :sunglasses:

9年前 评论
Summer

呦吼呦吼呦吼. 说好的更新呢 :sunglasses:

9年前 评论
Summer

已更新.

9年前 评论
Summer

@JobsLong 解析器出现的问题, 请见这里 http://phphub.org/topics/123 , 已经放在我的 Todo list 里面了.

9年前 评论

晓文大神啊啊啊啊啊啊:+1:

7年前 评论
StringKe

文章最后一处拼写错误 HTPP

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!