客户端

利用 api:getapi:post 特殊属性 ,当对应的组件被点击之后,客户端将向指定的 api 地址发起请求,请求返回的数据可以用于重新填充模板更新界面。

公共请求参数

公共参数以 Query Parameter 的方式传递:
为了避免公共参数和你自定的参数混淆,客户端发起的请求中,以 _ 作为前缀的参数表示是超级消息定义的,因此,对于您自己的定义的参数不要使用 _ 作为参数前缀

/path?_id=xxx&_rt=xxxx&_cid=xxxx&_tid=xxxx&_tv=1
字段名称类型必传接口版本号说明
_idint64≥ 1消息 ID
_lidint64≥ 1本地消息 ID,是客户端本地生成(非推送)的消息 local ID 必大于 0
_rtstring≥ 1request token
_cidstring≥ 1频道 ID
_tidstring≥ 1模板 ID,您在后台创建的模板
_tvint32≥ 1模板版本号
_vint32≥ 1客户端接口版本号(非客户端版本号)

注意,无论何种请求,你都应该首先验证请求的合法性!方法查看验证请求的合法性

接口版本号

  • 为什么会有接口版本号?

    • 随着以后客户端不断的发布新版本,客户端接口能处理的功能将越来越多,而市场上将必然同时存在多个版本的客户端,其接口处理能力不一。 为了方便开发者能识别客户端接口处理能力,故设计了接口版本号的字段,希望在有些情况上能帮得上忙。
  • 我是不是一定要处理这个版本号?

    • 大部分情况下都可以不用处理。
    • 接口的改动会尽量兼容以往的设计,忽略接口版本号,默认会使用客户端能用到的最新版本的处理方法来处理(即当前客户端拥有的功能都发挥作用)。

公共响应数据约定

此部分内容是客户端约定好的可以处理的数据结构,在处理完请求后你应该按照下面的格式返回正确的结构:

  • HTTP Status

    只有当响应 200 OK 的时候,客户端才会继续处理返回的数据,返回其它状态码当错误处理。

  • HTTP Body 格式

    {
        // 删除频道中一条[消息],可选
        // 此功能适合一些不需要保存的[消息],保持消息流干净
        "delete": {
            "id": int64,     // 消息 id,必传有效值
            "localID": int64 // 本地消息 local id,必传有效值
        },

        // 替换更新[消息]整个 data 字段,更新会持久化到本地,可选
        "update": {
            "id": int64,      // 消息 id,必传有效值
            "localID": int64, // 本地消息 local id,必传有效值
            "title": string,  // 消息标题,不传或传 null 表示不更新标题
            "data": Object,   // 消息数据,可选,map<string, any> 类型
            "tid": string,    // 模板 id,必传有效值
            "tv": int32       // 模板版本号,必传有效值
        },

        // 仅在内存中更新当前 data 中部分字段,不会持久化到本地,重新进入频道重启应用即消失
        // 具体操作见 ops 字段中的 Operation
        // updatePart 只能用于更新用户当前正在操作的 [消息]/[Dialog](见 Dialog 组件) 的数据
        // 如果 上面的 update 字段有值,并且更新的[消息]也是当前的[消息],那么 updatePart 
        // 字段将被忽略
        // 如果在[Dialog]中,想要修改变量,只能通过返回 updatePart
        "updatePart": {
            // 用于更新 data,每一步更新后的结果都是下一步的输入,伪代码形式:
            // ops = [op1, op2, op3, ...]
            // data = op1(data)
            // data = op2(data)
            // data = op3(data)
            // ...
            "ops": [Operation, ...],
            //  [Dialog] 组件 pullUp:xx 属性中(通常用于上拉加载下一页数据),如果请求返回结果中此字段为 true,则表示没有新数据,
            // 此时客户端会显示没有更多数据,不会再发起请求
            "noMoreContents": bool,
            // ops 如果有多个操作步骤,如果中间某一步出错了,是否忽略错误继续执行下去,默认为 false,中断
            "ignoreError": bool,
        },

        // 生成一条新[消息],可选
        "new": {
            "tid": string,   // 模板 id,必传有效值
            "tv": int32,     // 模板版本号,必传有效值
            "title": string, // 消息标题,必传有效值,不能为空字符串
            "data": Object   // 消息数据,可选,map<string, any> 类型
        },

        // 展示提示信息,可选
        "dismiss": {
            "type": int32,     // 提示信息类型,可选;0 = 普通信息(默认),1 = 成功信息,2 = 警告信息,3 = 错误信息
            "tip": string,     // 提示信息内容,必传有效值,不能为空字符串,不建议太长的文本 
            "duration": int32, // 展示时长,单位毫秒,可选,默认 3000,最大 30000
        },

        "version": int32,
    }

执行顺序是 deleteupdateupdatePartnewdismiss

  • Operation
    一共四种操作,分别是:

    • $set
      将指定的字段设置/替换为新值
    • $unset
      将指定的字段从 data 中删除
    • $insert
      向指定的字段(必须是数组)插入元素
    • $remove
      将指定的字段(必须是数组)的元素移除(数组长度缩短,如果只是设置为 null 应该使用 $set)
  • keypath
    keypath 是将一系列嵌套的 key 名称用 . 号串联起来的字符串,就像在某些常用编程语言访问对象深层字段的形式一样,用于精确定位字段位置。 比如 a.b.c 相当于访问 data["a"]["b"]["c"]list[0].users[3] 相当于访问 data["list"][0]["users"][3]

    如果 keypath 中的某个 key 不存在:

    1. 中间的 key 不存在,则报错,比如 a.b.c 中的 ab 不存在,则无法继续执行
    2. 如果是最后一个 key 不存在:
      • $set 可以正常操作
      • $unset 可以正常操作
      • $insert,如果没有指定 $index,相当于 push 行为,当成空数组 push,如果指定了插入的 $index,则无法执行
      • $remove,无法操作
  1. $set 将指定的字段设置/替换为新值
// 形式
    {"$set": {keypath1: newValue1, keypath2: newValue2, ...}}

// 举例
    {
        "$set": {
            "a.b.c": 100, // data["a"]["b"]["c"] = 100
            "list[0].users[3]": {...} // // data["list"][0]["users"][3] = {...}
        }
    }

// 关于操作顺序:
// 单个 $set 操作里面是不保证先后顺序的,如果需要先后顺序,将其单独定义,放到 ops 下面
    {
        "updatePart": {
            // ops 指多个操作步骤,操作顺序按照数组的顺序来
            "ops": [
                {"$set": {"a.b.c": 100}},
                {"$set": {"list[0].users[3]": {...}}}
            ]
        }
    }
  1. $unset
    将指定的字段从 data 中删除,也可以看成是(设置为 null 时) $set 的简化操作
// 形式
    {"$unset": [keypath, ...]}

// 举例
    {
        "$unset": [
            "a.b.c", // data["a"]["b"]["c"] = null
            "list[0].users[3]": // 相当于 data["list"][0]["users"][3] = null,但是不会移除数组元素
        ]
    }
  1. $insert (数组) 将指定数量的元素插入到数组的指定位置
// 形式
    {
        "$insert": {
            "$keypath": keypath,
            // 等待插入到对应位置的元素
            "$ele": [Object, ...],
            // 可选,未设置或小于 0 时,等同于 push 到数组尾端,大于 0 
            // 设置了大于 0 且在索引范围内,则将上面的元素插入指定索引的地方,后面的元素往尾移动
            // 如果索引超出范围则无法执行操作
            "$index": 1
        }
    }

// 举例
    data = {
        "list": [
            {"users": [1, 2]}, // <- 准备修改的数组
            {"users": [3, 4]},
        ]
    }
// 执行 $insert 操作
    {
        "$insert": {
            "$keypath": "list[0].users",
            "$ele": ["bb1", "bb2"],
            "$index": 1
        }
    }
// 结果
    data = {
        "list": [
            {"users": [1, "bb1", "bb2", 2]}, // <- 修改后的数组
            // {"users": [1, 2, "bb1", "bb2"]}, // <- 如果 $index < 0 或不传,则等同于 push 行为
            {"users": [3, 4]},
        ]
    }
  1. $remove (数组) 将数组中指定索引位置的元素移出数组,同时数组长度减少
// 形式
    {
        "$remove": {
            "$keypath": keypath,
            "$indexes": [index, ...] // 准备移除的元素的索引的位置
        }
    }

// 举例
    data = {
        "list": [
            {"users": [1, 2, 3, 4, 5, 6, 7]}, // <- 准备修改的数组
            {"users": [3, 4]},
        ]
    }
// 执行 $remove 操作
    {
        "$remove": {
            "$keypath": "list[0].users",
            "$indexes": [0, 1, 5, 6]
        }
    }
// 结果
    data = {
        "list": [
            {"users": [3, 4, 5]}, // <- 修改后的数组
            {"users": [3, 4]},
        ]
    }
注意,每个 **Operation** 只能定义其中一种操作:
    // 正确的定义
    {
        "ops": [
            {"$insert": ...},
            {"$set": ...}
            {"$set": ...}
            {"$remove": ...}
        ]
    }

    // 错误的定义
    {
        "ops": [
            {
                // 不能一次定义多个
                "$insert": ...,
                "$set": ...,
                "$remove": ...,
            }
            {"$set": ...}
        ]
    }
完整的例子
    data = {
        "list": [
            {"title": "aaaa", "chekced": true, "users": ["aa", "bb", "cc"]},
            {"title": "bbbb", "chekced": true}
        ],
        "total": 23
    }

    // 接口返回
    {
        "updatePart": {
            "ops": [
                {
                    "$set": {
                        // "users": ["aa", "bb", "cc"] => "users": ["aa", "bb", "CC"]
                        "list[0].users[2]": "CC",
                        // "title": "aaaa" => "title": "AAAA"
                        "list[0].title": "AAAA",
                        // "title": "bbbb" => "title": "BBBB",
                        "list[1].title": "BBBB",
                        // 新增字段 "created": "2020-04-01"
                        "list[1].created": "2020-04-01",
                        // "chekced": true => "chekced": "2020-04-01"
                        "list[0].chekced": "2020-04-01"
                    }
                },
                {
                    // 删除前两个元素 "users": ["aa", "bb", "CC"] => "users": ["CC"]
                    "$remove": [
                        "$keypath": "list[0].users",
                        "$indexes": [0, 1]
                    ]
                },
                {
                    // 在前面插入另外两个元素 "users": ["CC"] => "users": ["AA", "BB", "CC"]
                    "$insert": [
                        "$keypath": "list[0].users",
                        "$ele": ["AA", "BB"],
                        "$index": 0
                    ]
                },
                {
                    // push 操作 "users": ["AA", "BB", "CC"] => "users": ["AA", "BB", "CC", "DD", "EE"]
                    "$insert": [
                        "$keypath": "list[0].users",
                        "$ele": ["DD", "EE"],
                    ]
                },
                // 移除字段
                {"$unset": ["total"]} // total: 23 => total: null
            ]
        }
    }
    
    // 执行结果
    {
        "list": [
            {"title": "AAAA", "chekced": "2020-04-01", "users": ["AA", "BB", "CC", "DD", "EE"]},
            {"title": "BBBB", "chekced": false, "created": "2020-04-01"}
        ]
    }