开发规范

2018-03-02  本文已影响0人  lkning

Web后端规范

不久将来,整个行业存在的唯一目的就是消费平台上的数据。你的API越容易使用,那么就 会有越多的人去用它。立足现在,展望未来,restful风格的api是我们必然的趋势。

定义

URL

URI 表示资源,资源一般对应服务器端领域模型中的实体类。

URL规范

  1. 不用大写字符
  2. 只用下划线_连接
  3. 名词表示资源的集合,使用复数

资源集合,单个资源

资源集合

/zooms/  # 所有动物园
/zooms/1/employees/  # id为1的动物园的所有职工

单个资源

/zooms/1/  # id为1的动物园

避免层级过深的url

/在url中表达层级,用于按实体关联关系进行对象导航,一般根据id导航。

过深的导航容易导致url膨胀,不易维护,如 GET /zooms/1/areas/3/animals/4, 尽量使用查询参数代替路径中的实体导航,如GET /animals/?zoom=1&area=3。

Request

动词

五个非常重要的HTTP动词必须知道

原则上,只允许客户端或者第三方调用者使用这五个HTTP动词进行数据交互,并且在URL段 里面不出现任何其他的动词。有写操作的动词性很强, 不太容易用一个名词来表述,可以 逻辑上换一个方式来处理,比如作废一张订单,可以采用两种方式来实现。一是修改订单的 状态和相关字段(PATCH方法,这种的业务逻辑性不强,不清晰,不推荐),另一种是把作废 的订单看作一种逻辑资源,只需要在这种资源集合中使用POST方法创建一个资源即可。

另外一般情况下,GET请求要考虑缓存(客户端,服务器都要考虑),减少服务器检索数据的 压力。

版本化

API是服务器与客户端之间的一个公共契约。对服务器上的API做了一个更改,并且这些更改 无法向后兼容,那么就打破了这个契约。这时候既要确保应用程序逐步的演变,又要让客户 端继续可用。那么必须在引入新版本API的同时保持旧版本API仍然可用。所以接口必须版本 化

  1. 如果只是简单的增加一个新的特性到API上,如资源上的一个新属性或者增加一个新的端 点,不需要增加API的版本。因为这些并不会造成向后兼容性的问题,只需要修改文档即可。
  2. 申明不支持一个特性并不意味着关闭或者破坏它。而是告诉客户端旧版本的API将在某个 特定的时间被删除,并且建议他们使用新版本的API。
  3. 在URL中包含版本信息 GET /v1/zooms/ GET /v2/zooms/1/animals/

UserAgent

所有请求的Header信息中必须按照格式填写User-Agent信息,格式:

type|os|os_version|product|version|...

说明:

手机端必须增加 手机品牌|手机型号|屏幕分辨率 没有的用空表示;必须包含前面五个字段。例如:

"User-Agent": "MOBILE|Android|7.0|KLICEN_APP|2.2.5|Dalvik/2.1.0 (Linux; U; Android 7.0; FRD-AL00 Build/HUAWEIFRD-AL00)|1080*1794|863549034263964"

Response

内容类型

接口只提供两种类型的返回内容

使用ISO8601的国际化时间格式

接收和返回时间数据时只使用UTC格式

"create_at": "2012-01-01T12:00:00Z"

枚举型

枚举类型的字段返回其名值对:

{
    "key": 存储值,
    "verbose": 显示值
}

提供标准的时间戳

提供默认的资源创建时间,更新时间 created_at and updated_at, 例如:

{
  ...
  "created_at": "2012-01-01T12:00:00Z",
  "updated_at": "2012-01-01T13:00:00Z",
  ...
}

这些时间戳可能不适用于某些资源,这种情况下可以忽略省去。

包装

response 的 body 统一做如下包装。示例:

{
    "code": 0,
    "msg": "请求成功"(用户友好),
    "data": {
        "id": 1,
        "name": "张三"
    }
}

使用djangorestframework的情况下,可以在配置文件中配置REST_FRAMEWORK中的DEFAULT_RENDERER_CLASSESutils.renders.CustomJsonRender, 它的具体实现如下:

# -*- coding: utf-8 -*-
from rest_framework.renderers import JSONRenderer

class CustomJsonRender(JSONRenderer):
    """ 自定义返回数据 Json格式
    {
        "code": 0,
        "msg": "success",
        "data": { ... }
    }
    """

    def render(self, data, accepted_media_type=None, renderer_context=None):
        if renderer_context:
            response = renderer_context['response']
            code = 0 if int(response.status_code / 100) == 2 else response.status_code
            msg = 'success'
            if isinstance(data, dict):
                msg = data.pop('msg', msg)
                code = data.pop('code', code)
                data = data.pop('data', data)
            if code != 0 and data:
                msg = data.pop('detail', 'failed')
            response.status_code = 200
            res = {
                'code': code,
                'msg': msg,
                'data': data,
            }
            return super().render(res, accepted_media_type, renderer_context)
        else:
            return super().render(data, accepted_media_type, renderer_context)

可能的错误信息的code可根据实际情况扩展, 所以要让客户端能获取到这些code

code码

嵌套外键关系

序列化的外键关系建立在一个有嵌套关系的对象之上, 例如:

{
  "name": "成都动物园",
  "manager": {
    "id": 1
  },
  ...
}

而不是这样, 例如:

{
  "name": "成都动物园",
  "owner_id": 1,
  ...
}

这种方式把相关联的资源信息内联在一起,在返回更多信息时不用改变响应资源的结构, 例如:

{
  "name": "成都动物园",
  "owner": {
    "id": 1,
    "name": "张三",
    "email": "zhangsan@klicen.com"
  },
  ...
}

文档

如果时间允许,甚至可以创建一个控制台来让开发者可以立即体验一下API的功能。

API分析

API分析就是持续跟踪那些正为人使用的API的版本和端点信息,这样做的好处是:

  1. 决定什么时候不再支持某个版本
  2. 找到那些使用最广泛的API,用其作为指导业务方向或者优化效率的重要的依据
  3. 更多...

认证与安全

认证

采用OAuth2.0,OAuth2.0提供了一个非常好的方法去做这件事。在每一个请求里, 你可以明确知道哪个客户端创建了请求,哪个用户提交了请求,并且提供了一种标准的访问 过期机制或允许用户从客户端注销,所有这些都不需要第三方的客户端知道用户的登陆认证 信息。

安全

这个暂时不是当前的重点,这里只列出几个方向,后期再完善

上一篇 下一篇

猜你喜欢

热点阅读