API 接口设计规范

协议

API 与客户端通讯协议主要包含 httphttps,建议使用 https 确保交互数据的传输安全。

域名

两种形式:

  • 主域名:https://api.demo.com
  • 子目录:https://demo.org/api/

    版本控制

    主要用于 App、微信小程序、软件客户端等与系统不同时实时更新的情况,来满足需兼容旧版本的场景。采用多版本并存,增量发布的方式。

版本号:v {n} n 代表版本号,分为整形和浮点型 整型:大功能版本发布形式;具有当前版本状态下的所有 API 接口,例如:v1,v2 浮点型:为小版本号,只具备补充 api 的功能,其他 api 都默认调用对应大版本号的 api 例如:v1.1 v2.2

  • 版本号放入 URL 中:https://api.demo.com/v{n}/,比较方便和直观,版本号主要以整型为主。

  • 版本号放在请求头(Request Headers)中,可使用整型、浮点型等

    1
    2
    3
    4
    headers:{
    version: 'v{n}'
    ...
    }

    路径规则

    路径又称 “终点”(endpoint),表示 API 的具体网址。
    在 RESTful 架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的 “集合”(collection),所以 API 中的名词也应该使用复数。
    有一个 API 提供学校(school)的信息,还包括学校和班级、学生的信息,则它的路径应该设计成下面这样。

    1
    2
    3
    https://api.demo.com/v1/school
    https://api.demo.com/v1/classes
    https://api.demo.com/v1/students

    请求方式

    对于资源的具体操作类型,由 HTTP 动词表示。常用的 HTTP 动词有下面四个(括号里是对应的 SQL 命令)。

    1
    2
    3
    4
    GET(SELECT):从服务器取出资源(一项或多项)。
    POST(CREATE):在服务器新建一个资源。
    PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
    DELETE(DELETE):从服务器删除资源。

    例如:

    1
    2
    3
    4
    5
    6
    7
    GET /classes:列出所有班级
    POST /classes:新建一个班级
    GET /classes/ID:获取某个指定班级的信息
    PUT /classes/ID:更新某个指定班级的信息
    DELETE /classes/ID:删除某个班级
    GET /classes/ID/students :列出某个指定班级的所有学生
    GET /classes/ID/students/ID:获取某个指定班级的指定学生信息

    传入参数

    传入参数分为 3 中类型

    地址栏参数

  • restful 地址栏参数 /api/v1/students/22 22 为学生编号,获取学生编号为 22 的信息

  • get 方式的查询字串,此种方式主要用于过滤查询,如下:

    1
    2
    3
    4
    5
    ?limit=10:指定返回记录的数量
    ?offset=10:指定返回记录的开始位置。
    ?page=2&per_page=100:指定第几页,以及每页的记录数。
    ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
    ?producy_type=1:指定筛选条件

    请求 body 数据

    用于提交新建及更新的数据。

    请求头

    用于存放请求格式信息、版本号、token 密钥、语言等信息。

    1
    2
    3
    4
    5
    6
    {
    Accept: 'application/json', //json格式
    version: 'v1.0' //版本号
    Authorization: 'Bearer {_token}', //认证token
    language: 'zh' //语言
    }

    返回格式:

    1
    2
    3
    4
    5
    {
    code: 0, //状态码
    msg: '成功...', //提示信息
    data: {} //主体数据
    }

    使用 json 格式作为响应格式,状态码分为两种:

  • statusCode: 系统状态码,用于处理响应状态,与 http 状态码保持一致,如:200 表示请求成功,500 表示服务器错误。

  • code:业务状态码,用于处理业务状态,一般 0 为成功,-1为失败,可参考微信开发文档

非 Restful API 的需求

一般以 Restful Api 作为接口规范,但由于实际业务开展过程中,可能会出现各种的 api 不是简单的 restful 规范能实现的,因此需要有一些 api 突破 restful 规范原则。特别是移动互联网的 api 设计,更需要有一些特定的 api 来优化数据请求的交互。

单例型:

客户端根据需求分别请求对应 Api 接口,在客户端完成组装。这种模式服务端相对简单,接口复用率高。每个接口作用单一,轮播图、分类,则需要分别请求:

1
2
/api/v1/banners: 轮播
/api/v1/categories: 分类

Laravel API 开发

  • 创建UserController控制器

    1
    php artisan make:controller Demo\UserController --resource

    创建普通控制器可以去掉 –resource

  • 打开routes/api.php 配置路由

    1
    2
    3
    4
    5
    6
    //restful 接口风格使用
    Route::get('v1/restfuls', [UserController::class, 'index']);//列表
    Route::get('v1/restfuls/{id}', [UserController::class, 'show']);//获取单条
    Route::post('v1/restfuls', [UserController::class, 'create']);//录入
    Route::put('v1/restfuls/{id}', [UserController::class, 'update']);//修改
    Route::delete('v1/restfuls/{id}', [UserController::class, 'delete']);//删除
  • 控制器代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    <?php

    namespace App\Http\Controllers\Demo;

    use App\Http\Controllers\Controller;
    use App\Models\User;
    use Illuminate\Http\Request;

    class UserController extends Controller
    {
    /**
    * Notes: 查询列表
    * User: 62726
    * Date: 2020/10/29
    * Time: 17:31
    * @return array
    */
    public function index()
    {
    try {
    $data = User::all()->toArray();
    } catch (\Exception $e) {
    return ['code' => -1, 'msg' => '错误信息:' . substr($e, 1, 250) . '...', 'data' => ''];
    }
    return ['code' => 0, 'msg' => '成功', 'data' => $data];
    }

    /**
    * Notes: 查询单条
    * User: 62726
    * Date: 2020/10/29
    * Time: 17:32
    * @param $id
    * @return mixed
    */
    public function show($id)
    {
    try {
    $data = User::find($id)->toArray();
    } catch (\Exception $e) {
    return ['code' => -1, 'msg' => '错误信息:' . substr($e, 1, 250) . '...', 'data' => ''];
    }
    return ['code' => 0, 'msg' => '查询成功', 'data' => $data];
    }


    /**
    * Notes: 新增接口
    * User: 62726
    * Date: 2020/10/29
    * Time: 18:23
    * @param Request $request
    * @return string
    */
    public function create(Request $request)
    {
    $name = $request->input('name');
    $email = $request->input('email');
    $password = $request->input('password');
    try {
    $user = new User();
    $user->name = $name;
    $user->email = $email;
    $user->password = bcrypt($password);
    $user->save();
    } catch (\Exception $e) {
    return response(['code' => -1, 'msg' => '错误信息:' . substr($e, 1, 250) . '...', 'data' => '']);
    }
    return response(['code' => 0, 'msg' => '新建成功', 'data' => '']);
    }

    /**
    * Notes: 更新接口
    * User: 62726
    * Date: 2020/10/29
    * Time: 18:51
    * @param Request $request
    * @return string
    */
    public function update(Request $request)
    {
    $id = (int)substr($request->path(), 16);
    $name = $request->input('name');
    $email = $request->input('email');
    $password = $request->input('password');
    try {
    $user = User::find($id);
    $user->name = $name;
    $user->email = $email;
    $user->password = bcrypt($password);
    $user->save();
    } catch (\Exception $e) {
    return response(['code' => -1, 'msg' => '错误信息:' . substr($e, 1, 250) . '...', 'data' => '']);
    }
    return response(['code' => 0, 'msg' => '更新成功', 'data' => '']);
    }

    /**
    * Notes: 删除接口
    * User: 62726
    * Date: 2020/10/30
    * Time: 8:18
    * @param $id
    * @return string
    */
    public function delete($id)
    {
    try {
    $data = User::find($id);
    $data->delete();
    } catch (\Exception $e) {
    return response(['code' => -1, 'msg' => '错误信息:' . substr($e, 1, 250) . '...', 'data' => '']);
    }
    return response(['code' => 0, 'msg' => '删除成功', 'data' => '']);
    }
    }
  • 测试

  • 注意

    • 请求方式: PUT
    • Content-Type: application/x-www-form-urlencoded
  • 参考