存储 REST API
REST API 可以让你用任何支持发送 HTTP 请求的设备来与云服务进行交互,你可以使用 REST API 做很多事情,比如:
- 使用任何编程语言操作云端数据。
- 如果你不再需要使用云服务,你可以导出你所有的数据。
- 一个追求最少化依赖库的应用可以不引入 SDK,直接访问 REST API 获取云服务上的数据。
- 你可以批量新增大量数据,供应用之后读取。
- 你可以下载最近的数据用于离线分析和归档备份。
API 版本
当前的 API 版本是 1.1
。
在线测试
为了方便测试 REST API,文档给出了 curl 命令示例,示例针对类 unix 平台(macOS、Linux 等)编写,直接粘贴至 Windows 平台 cmd.exe 很可能无法工作。
例如,curl 命令示例中的 shell 换行符(\
)在 cmd.exe 中是目录分隔符。
Windows 平台建议使用 Postman 等客户端测试。
点击展开 Postman 示例
Postman 可直接导入 curl 命令。
Postman 还支持自动生成多种语言(库)调用 REST API 的代码。
Base URL
REST API 请求的 Base URL(下文 curl 示例中用 {{host}}
表示)即应用绑定的 API 自定义域名,可以在 开发者中心 > 你的游戏 > 游戏服务 > 应用配置 > 域名配置 绑定、查看。
对象
URL | HTTP | 功能 |
---|---|---|
/1.1/classes/<className> | POST | 创建对象 |
/1.1/classes/<className>/<objectId> | GET | 获取对象 |
/1.1/classes/<className>/<objectId> | PUT | 更新对象 |
/1.1/classes/<className> | GET | 查询对象 |
/1.1/classes/<className>/<objectId> | DELETE | 删除对象 |
/1.1/scan/classes/<className> | GET | 按照特定顺序遍历 Class |
角色
URL | HTTP | 功能 |
---|---|---|
/1.1/roles | POST | 创建角色 |
/1.1/roles/<objectId> | GET | 获取角色 |
/1.1/roles/<objectId> | PUT | 更新角色 |
/1.1/roles | GET | 查询角色 |
/1.1/roles/<objectId> | DELETE | 删除角色 |
数据 Schema
URL | HTTP | 功能 |
---|---|---|
/1.1/schemas | GET | 获取应用所有 Class 的 Schema |
/1.1/schemas/<className> | GET | 获取应用指定 Class 的 Schema |
其他 API
URL | HTTP | 功能 |
---|---|---|
/1.1/date | GET | 获得服务端当前时间 |
/1.1/exportData | POST | 请求导出应用数据 |
/1.1/exportData/<id> | GET | 获取导出数据任务状态和结果 |
应用凭证
在 开发者中心 > 你的游戏 > 游戏服务 > 应用配置 可以查看应用的基本信息:
- Client ID:又称
App ID
,在 SDK 初始化时用到。 - Client Token:又称
App Key
,客户端对服务端的调用凭证,在 SDK 初始化时用到。 - 域名配置 > 云服务 API:又称 API 域名或 Server URL,在客户端 SDK 初始化时用到。域名配置参考下一节域名。
- Server Secret:又称
Master Key
,用于在自有服务器、云引擎等受信任环境调用管理接口,具备跳过一切权限验证的超级权限。所以一定注意保密,千万不要在客户端代码中使用该凭证。
请求格式
对于 POST 和 PUT 请求,请求的主体必须是 JSON 格式,而且 HTTP header 的 Content-Type
需要设置为 application/json
。
用户验证通过 HTTP header 来进行,X-LC-Id
标明正在运行的是哪个应用(应用的 App ID
,对应到开发者中心游戏的 Client ID
),X-LC-Key
用来授权鉴定 endpoint:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "更新一篇博客的内容"}' \
https://{{host}}/1.1/classes/Post/<objectId>
X-LC-Key
通常情况下是应用的 App Key
(对应到开发者中心游戏的 Client Token
),有些情况(需要超级权限的操作)下是应用的 Master Key
(对应到开发者中心游戏的 Server Secret
)。
当 X-LC-Key
值为 Master Key
时,需要在其后添加 ,master
后缀以示区分,例如:
X-LC-Key: {{masterkey}},master
对于 JavaScript 使用,云服务支持跨域资源共享,所以你可以将这些 header 同 XMLHttpRequest
一同使用。
REST API 通讯支持 gzip
和 brotli
压缩,客户端可以通过指定相应的 Accept-Encoding
HTTP 头开启压缩。
更安全的鉴权方式
我们还支持一种新的 API 鉴权方式,即在 HTTP header 中使用 X-LC-Sign
来代替 X-LC-Key
,以降低 App Key
的泄露风险。例如:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Sign: d5bcbb897e19b2f6633c716dfdfaf9be,1453014943466" \
-H "Content-Type: application/json" \
-d '{"content": "在 HTTP header 中使用 X-LC-Sign 来更新一篇博客的内容"}' \
https://{{host}}/1.1/classes/Post/<objectId>
X-LC-Sign
的值是由 sign,timestamp[,master]
组成的字符串:
取值 | 约束 | 描述 |
---|---|---|
sign | 必须 | 将 timestamp 加上 App Key 或 Master Key 组成的字符串,再对它做 MD5 签名后的结果。 |
timestamp | 必须 | 客户端产生本次请求的 unix 时间戳(UTC),精确到毫秒。 |
master | 可选 | 字符串 "master" ,当使用 master key 签名请求的时候,必须加上这个后缀明确说明是使用 master key。 |
注意,sign
中的 MD5 签名的 hex 值中的字母请使用小写。如果使用大写字母,会导致签名校验 失败。
举例来说,假设应用的信息如下:
App Id | FFnN2hso42Wego3pWq4X5qlu |
App Key | UtOCzqb67d3sN12Kts4URwy8 |
Master Key | DyJegPlemooo4X1tg94gQkw1 |
请求时间 | 2016-01-17 15:15:43.466 GMT+08:00 |
timestamp | 1453014943466 |
使用 App Key
来计算 sign:
md5( timestamp + App Key )
= md5(1453014943466UtOCzqb67d3sN12Kts4URwy8)
= d5bcbb897e19b2f6633c716dfdfaf9be
-H "X-LC-Sign: d5bcbb897e19b2f6633c716dfdfaf9be,1453014943466" \
使用 Master Key
来计算 sign:
md5( timestamp + Master Key )
= md5(1453014943466DyJegPlemooo4X1tg94gQkw1)
= e074720658078c898aa0d4b1b82bdf4b
-H "X-LC-Sign: e074720658078c898aa0d4b1b82bdf4b,1453014943466,master" \
(最后加上 ,master
来告诉服务器这个签名是使用 master key 生成的。)
使用 master key 将绕过所有权限校验,应该确保只在可控环境中使用,比如自行开发的管理平台,并且要完全避免泄露。 以上两种计算 sign 的方法可以根据实际情况来选择一种使用。
指定 hook 函数调用环境
请求可能触发云引擎的 hook 函数,可以通过设置 HTTP 头 X-LC-Prod
来区分调用的环境。
X-LC-Prod: 0
表示调用预备环境X-LC-Prod: 1
表示调用生产环境
默认(未指定 X-LC-Prod
头)调用生产环境的 hook 函数。
响应格式
对于所有的请求,响应格式都是一个 JSON 对象。
一个请求是否成功是由 HTTP 状态码标明的。一个 2XX 的状态码表示成功,而一个 4XX 表示请求失败。当一个请求失败时响应的主体仍然是一个 JSON 对象,但是总是会包含 code
和 error
这两个字段,你可以用它们来进行调试。举个例子,如果尝试用非法的属性名来保存一个对象会得到如下信息:
{
"code": 105,
"error": "Invalid key name. Keys are case-sensitive and 'a-zA-Z0-9_' are the only valid characters. The column is: 'invalid?'."
}
对象
对象格式
数据存储服务是建立在 LCObject(对象)基础上的,每个 LCObject 包含若干属性值对(key-value,也称「键值对」),属性的值是与 JSON 格式兼容的数据。 通过 REST API 保存对象需要将对象的数据通过 JSON 来编码。这个数据是无模式化的(schema free),这意味着你不需要提前标注每个对象上有哪些 key,你只需要随意设置键值对就可以,后端会保存它。
举个例子,假如我们要实现一个类似于微博的社交 App,主要有三类数据:账户、帖子、评论,一条 微博帖子可能包含下面几个属性:
{
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "官方客服",
"pubTimestamp": 1435541999
}
Key(属性名)必须是字母、数字、下划线组成的字符串,Value(属性值)可以是任何可以 JSON 编码的数据。
每个对象都有一个类名,你可以通过类名来区分不同的数据。例如,我们可以把微博的帖子对象称之为 Post。我们建议将类和属性名分别按照 NameYourClassesLikeThis
和 nameYourKeysLikeThis
这样的惯例来命名,即区分第一个字母的大小写,这样可以提高代码的可读性和可维护性。
当你从云端获取对象时,一些字段会被自动加上,如 createdAt
、updatedAt
和 objectId
。这些字段的名字是保留的,值也不允许修改。我们上面设置的对象在获取时应该是下面的样子:
{
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "官方客服",
"pubTimestamp": 1435541999,
"createdAt": "2015-06-29T01:39:35.931Z",
"updatedAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
createdAt
和 updatedAt
都是 UTC 时间戳,以 ISO 8601 标准和毫秒级精度储存:YYYY-MM-DDTHH:MM:SS.MMMZ
。objectId
是一个字符串,在类中可以唯一标识一个实例。
在 REST API 中,class 级的操作都是通过一个带类名的资源路径(URL)来标识的。例如,如果类名是 Post
,那么 class 的 URL 就是:
https://{{host}}/1.1/classes/Post
对于用户账户这种对象,有一个特殊的 URL:
https://{{host}}/1.1/users
针对于一个特定的对象的操作可以通过组织一个 URL 来做。例如,对 Post
中的一个 objectId
为 558e20cbe4b060308e3eb36c
的对象的操作应使用如下 URL:
https://{{host}}/1.1/classes/Post/558e20cbe4b060308e3eb36c
创建对象
创建一个新的对象,应该向 class 的 URL 发送一个 POST 请求,其中应该包含对象本身。 例如,要创建如上所说的对象:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "每个 Java 程序员必备的 8 个开发工具","pubUser": "官方客服","pubTimestamp": 1435541999}' \
https://{{host}}/1.1/classes/Post
当创建成功时,HTTP 的返回是 201 Created,而 header 中的 Location 表示新的 object 的 URL:
Status: 201 Created
Location: https://{{host}}/1.1/classes/Post/558e20cbe4b060308e3eb36c
响应的主体是一个 JSON 对象,包含新的对象的 objectId 和 createdAt 时间戳。
{
"createdAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
如果希望返回新创建的对象的完整信息,可以在 URL 里加上 fetchWhenSave
选项,并且设置为 true:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "每个 Java 程序员必备的 8 个开发工具","pubUser": "官方客服","pubTimestamp": 1435541999}' \
https://{{host}}/1.1/classes/Post?fetchWhenSave=true
Class 名称只能包含字母、数字、下划线。 **每个应用最多可以创建 500 个 class,每个 class 最多包含 300 个字段,**但每个 class 中的记录数量没有限制。
获取对象
当你创建了一个对象时,你可以通过发送一个 GET 请求到返回的 header 的 Location 以获取它的内容。例如,为了得到我们上面创建的对象:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/classes/Post/558e20cbe4b060308e3eb36c
返回的主体是一个 JSON 对象包含所有用户提供的 field 加上 createdAt、updatedAt 和 objectId 字段:
{
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "官方客服",
"pubTimestamp": 1435541999,
"createdAt": "2015-06-29T01:39:35.931Z",
"updatedAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
当获取的对象有指向其子对象的指针时,你可以加入 include
选项来获取这些子对象。假设微博记录中有一个字段 author
来指向发布者的账户信息,按上面的例子,可以这样来连带获取发布者完整信息:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'include=author' \
https://{{host}}/1.1/classes/Post/<objectId>
include
支持点号,例如,假定发布者有一个字段 department
指向发布者所属的部门,那么可以使用 include=author.department
一并获取部门信息。
类不存在时,返回 404 Not Found 错误:
{
"code": 101,
"error": "Class or object doesn't exists."
}
objectId 不存在时,返回一个空对象(HTTP 状态码为 200 OK):
{}
某些特殊的系统内置类(类名以下划线开头),objectId 不存在时不一定返回空对象。
例如,查询 _User
时,objectId 不存在会返回 400 Bad Request 错误:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://{{host}}/1.1/classes/_User/<NonexistObjectId>
返回:
{
"code": 211,
"error": "Could not find user."
}
顺带提一下,获取用户推荐使用 GET /users/<objectId>
,而不是直接查询 _User
类。
参见后文获取用户一节。
更新对象
为了更改一个对象已经有的数据,你可以发送一个 PUT 请求到对象相应的 URL 上,任何你未指定的 key 都不会更改,所以你可以只更新对象数据的一个子集。例如,我们来更改我们对象的一个 content 字段:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "每个 JavaScript 程序员必备的 8 个开发工具:http://buzzorange.com/techorange/2015/03/03/9-javascript-ide-editor/"}' \
https://{{host}}/1.1/classes/Post/<objectId>
返回的 JSON 对象只会包含一个 updatedAt 字段,表明更新发生的时间:
{
"updatedAt": "2015-06-30T18:02:52.248Z"
}
fetchWhenSave
选项对更新对象也同样有效。
但和创建对象不同,用于更新对象时仅返回更新的字段,而非全部字段。
计数器
比如一条微博,我们需要记录有多少人喜欢或者转发了它,但可能很多次喜欢都是同时发生的,如果每个客户端都直接把读到的计数值更改之后再写回去,那么极容易引发冲突和覆盖,导致最终结果不准。 云服务提供了对数字类型字段进行原子增加或者减少的功能,稳妥地实现对计数器类型数据的更新:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"upvotes":{"__op":"Increment","amount":1}}' \
https://{{host}}/1.1/classes/Post/<objectId>