Parse Rest API
REST API 可以让你用任何支持发送 HTTP 请求的设备来与 Parse 进行交互,你可以使用 REST API 做很多事情,比如:
- 一个移动网站可以通过 JavaScript 来获取 Parse上的数据.
- 一个网站可以展示来自 Parse的数据。
对象
对象格式
Parse 的数据存储服务是建立在 AVObject(对象)基础上的,每个 AVObject 包含若干属性值对(key-value,也称「键值对」),属性的值是与 JSON 格式兼容的数据。 通过 REST API 保存对象需要将对象的数据通过 JSON 来编码。这个数据是无模式化的(Schema Free),这意味着你不需要提前标注每个对象上有哪些 key,你只需要随意设置 key-value 对就可以,后端会保存它。
举个例子,假如我们要实现一个类似于微博的社交 App,主要有三类数据:账户、帖子、评论,一条微博帖子可能包含下面几个属性:
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "Parse官方客服",
"pubTimestamp": 123456789
Key(属性名)必须是字母和数字组成的字符串,Value(属性值)可以是任何可以 JSON 编码的数据。
每个对象都有一个类名,你可以通过类名来区分不同的数据。例如,我们可以把微博的帖子对象称之为 Post。我们建议将类和属性名分别按照 NameYourClassesLikeThis 和 nameYourKeysLikeThis 这样的惯例来命名,即区分第一个字母的大小写,这样可以提高代码的可读性和可维护性。
当你从 Parse 中获取对象时,一些字段会被自动加上,如 createdAt、updatedAt 和 objectId。这些字段的名字是保留的,值也不允许修改。我们上面设置的对象在获取时应该是下面的样子:
{
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "Parse官方客服",
"pubTimestamp": 123456789,
"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 就是:
对于用户账户这种对象,有一个特殊的 URL:
针对于一个特定的对象的操作可以通过组织一个 URL 来做。例如,对 Post 中的一个 objectId 为 558e20cbe4b060308e3eb36c 的对象的操作应使用如下 URL:
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
创建对象
为了在 Parse 上创建一个新的对象,应该向 class 的 URL 发送一个 POST 请求,其中应该包含对象本身。class 的名称必须以字母开头,只能包含字母、数字和下划线。例如,要创建如上所说的对象:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"content": "每个 Java 程序员必备的 8 个开发工具","pubUser": "Parse官方客服","pubTimestamp": 123456789}' \
https://api.parse.cn/1.1/classes/Post
当创建成功时,HTTP 的返回是 201 Created,而 header 中的 Location 表示新的 object 的 URL:
Status: 201 Created
Location: https://api.parse.cn/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": "parse官方客服","pubTimestamp": 1435541999}' \
https://api.parse.cn/1.1/classes/Post?fetchWhenSave=true
fetchWhenSave 选项对更新对象也同样有效,但是它仅返回已被更新的字段,而非全部字段。
每个应用最多可以创建 500 个 class,但每个 class 中的记录数量没有限制。
获取对象
当你创建了一个对象时,你可以通过发送一个 GET 请求到返回的 header 的 Location 以获取它的内容。例如,为了得到我们上面创建的对象:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
返回的主体是一个 JSON 对象包含所有用户提供的 field 加上 createdAt、updatedAt 和 objectId 字段:
{
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "parse官方客服",
"pubTimestamp": 123456789,
"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://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
更新对象
为了更改一个对象已经有的数据,你可以发送一个 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://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
返回的 JSON 对象只会包含一个 updatedAt 字段,表明更新发生的时间:
{
"updatedAt": "2015-06-30T18:02:52.248Z"
}
计算器
比如一条微博,我们需要记录有多少人喜欢或者转发了它,但可能很多次喜欢都是同时发生的,如果每个客户端都直接把读到的计数值更改之后再写回去,那么极容易引发冲突和覆盖,导致最终结果不准。parse提供了对数字类型字段进行原子增加或者减少的功能,稳妥地实现对计数器类型数据的更新:
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://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
这样就将对象的 upvotes 属性值(被用户点赞的次数)加上 1,其中 amount 为递增的数字大小,如果为负数,则为递减。
除了 Increment,我们也提供了 Decrement 用于递减,等价于 Increment 一个负数。
位算符
如果数据表的某一列是整型,可以使用位运算操作符该列进行原子的位运算:
- BitAnd 与运算
- BitOr 或运算
- BitXor 异或运算
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"flags":{"__op":"BitOr","value": 0x0000000000000004}}' \
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
数组
Parse 提供 3 种原子性操作来存储和更改数组类型的字段:
- Add:在一个数组字段的后面添加一些指定的对象(包装在一个数组内)
- AddUnique:只会在数组内原本没有这个对象的情形下才会添加入数组,插入的位置不定。
- Remove:从一个数组内移除所有的指定的对象
每种操作都有一个 key objects,其值为被添加或删除的对象列表。例如为每条微博增加一个「标签」属性 tags,然后往里面加入一些值:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"tags":{"__op":"AddUnique","objects":["Frontend","JavaScript"]}}' \
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
关系
Parse提供特殊的原子操作来添加和删除一个关系 Relation。比如用户喜欢了这条微博(添加关系):
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"likes":{"__op":"AddRelation","objects":[{"__type":"Pointer","className":"_User","objectId":"51c3ba67e4b0f0e851c16221"}]}}' \
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
用户取消了对这条微博的喜欢(删除关系):
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"likes":{"__op":"RemoveRelation","objects":[{"__type":"Pointer","className":"_User","objectId":"51fa3f64e4b05df1766cfb90"}]}}' \
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
按条件更新对象
假设从某个账户对象 Account 的余额中扣除一定金额,但是要求余额要大于等于被扣除的金额才允许操作,那么就需要通过 where 参数为更新操作加上限定条件 balance >= amount:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"balance":{"__op":"Decrement","amount": 30}}' \
"https://api.parse.cn/1.1/classes/Account/558e20cbe4b060308e3eb36c?where=%7B%22balance%22%3A%7B%22%24gte%22%3A%2030%7D%7D"
URL 中 where 参数的值是%7B%22balance%22%3A%7B%22%24gte%22%3A%2030%7D%7D,其实这是 **{"balance":{"$gte": 30}} **被 URL 编码后的结果。更多 where 查询的例子请参考 查询。
如果条件不满足,更新将失败,同时返回错误码 305:
{
"code" : 305,
"error": "No effect on updating/deleting a document."
}
特别强调:where 一定要作为 URL 的 Query Parameters 传入。
__op 操作汇总
使用** __op("操作名称", {JSON 参数})** 函数可以完成原子性操作,确保数据的一致性。
图表删除对象
为了在 LeanCloud 上删除一个对象,可以发送一个 DELETE 请求到指定的对象的 URL,比如:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
还可以使用 Delete 操作删除一个对象的一个字段(注意此时 HTTP Method 还是 PUT):
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"downvotes":{"__op":"Delete"}}' \
https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c
按条件删除对象
为请求增加 where 参数即可以按指定的条件来删除对象。例如删除点击量 clicks 为 0 的帖子:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
"https://api.parse.cn/1.1/classes/Post/558e20cbe4b060308e3eb36c?where=%7B%22clicks%22%3A%200%7D"
URL 中 where 参数的值是** %7B%22clicks%22%3A%200%7D,其实这是 {"clicks": 0}** 被 URL 编码后的结果。更多 where 查询的例子请参考 查询。
如果条件不满足,删除将失败,同时返回错误码** 305**:
{
"code" : 305,
"error": "No effect on updating/deleting a document."
}
特别强调:where 一定要作为 URL 的 **Query Parameters **传入。
遍历 Class
因为更新和删除都是基于单个对象的,都要求提供 objectId,但是有时候用户需要高效地遍历一个 Class,做一些批量的更新或者删除的操作。
通常情况下,如果 Class 的数量规模不大,使用查询加上** skip** 和** limit 分页配合排序 order **就可以遍历所有数据。但是当 Class 数量规模比较大的时候, skip 的效率就非常低了(这跟 MySQL 等关系数据库的原因一样,深度翻页比较慢),因此我们提供了 **scan 协议,可以按照特定字段排序来高效地遍历一张表,默认这个字段是 objectId 升序,同时支持设置 limit 限定每一批次的返回数量,默认 limit **为 100,最大可设置为 1000:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
https://api.parse.cn/1.1/scan/classes/Article
**scan **强制要求使用 master key。
返回:
{
"results":
[
{
"tags" : ["clojure","\u7b97\u6cd5"],
"createdAt": "2016-07-07T08:54:13.250Z",
"updatedAt": "2016-07-07T08:54:50.268Z",
"title" : "clojure persistent vector",
"objectId" : "577e18b50a2b580057469a5e"
},
...
],
"cursor": "pQRhIrac3AEpLzCA"}
其中 results 对应的就是返回的对象列表,而 cursor 表示本次遍历当前位置的「指针」,当 cursor 为 null 的时候,表示已经遍历完成,如果不为 null,请继续传入 cursor 到 **scan **接口就可以从上次到达的位置继续往后查找:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'cursor=pQRhIrac3AEpLzCA' \
https://api.leancloud.cn/1.1/scan/classes/Article
每次返回的** cursor **的有效期是 10 分钟。
遍历还支持过滤条件,加入 where 参数:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'where={"score": 100}' \
https://api.parse.cn/1.1/scan/classes/Article
默认情况下系统按** objectId **升序排序,增加 **scan_key **参数可以使用其他字段来排序:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'scan_key=score' \
https://api.parse.cn/1.1/scan/classes/Article
scan_key 也支持倒序,前面加个减号即可,例如 -score。
自定义的 scan_key 需要满足严格单调递增的条件,并且 **scan_key **不可作为 where 查询条件存在。
批量操作
为了减少网络交互的次数太多带来的时间浪费,你可以在一个请求中对多个对象进行 create、update、delete 操作。
在一个批次中每一个操作都有相应的方法、路径和主体,这些参数可以代替你通常会使用的 HTTP 方法。这些操作会以发送过去的顺序来执行,比如我们要一次发布一系列的微博:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{
"method": "POST",
"path": "/1.1/classes/Post",
"body": {
"content": "近期 parse 的文档已经支持评论功能,如果您有疑问、意见或是其他想法,都可以直接在我们文档中提出。",
"pubUser": "parse官方客服"
}
},
{
"method": "POST",
"path": "/1.1/classes/Post",
"body": {
"content": "很多用户表示很喜欢我们的文档风格,我们已将 parse所有文档的 Markdown 格式的源码开放出来。",
"pubUser": "parse官方客服"
}
}
]
}' \
https://api.parse.cn/1.1/batch
我们对每一批次中所包含的操作数量(requests 数组中的元素个数)暂不设限,但考虑到云端对每次请求的 body 内容大小有 20 MB 的限制,因此建议将每一批次的操作数量控制在 100 以内。
批量操作的响应 body 会是一个列表,列表的元素数量和顺序与给定的操作请求是一致的。每一个在列表中的元素都有一个字段是 success 或者 error。
[
{
"error": {
"code": 1,
"error": "Could not find object by id '558e20cbe4b060308e3eb36c' for class 'Post'."
}
},
{
"success": {
"updatedAt": "2017-02-22T06:35:29.419Z",
"objectId": "58ad2e850ce463006b217888"
}
}
]
需要注意,即使一个 batch 请求返回的响应码为 200,这仅代表服务端已收到并处理了这个请求,但并不说明该 batch 中的所有操作都成功完成,只有当返回 body 的列表中不存在 **error **元素,开发者才可以认为所有操作都已成功完成。
在 batch 操作中 update 和 delete 同样是有效的:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{
"method": "PUT",
"path": "/1.1/classes/Post/55a39634e4b0ed48f0c1845b",
"body": {
"upvotes": 2
}
},
{
"method": "DELETE",
"path": "/1.1/classes/Post/55a39634e4b0ed48f0c1845c"
}
]
}' \
https://api.parse.cn/1.1/batch
数据类型
到现在为止我们只使用了可以被标准 JSON 编码的值,LeanCloud 移动客户端 SDK library 同样支持日期、二进制数据和关系型数据。在 REST API 中,这些值都被编码了,同时有一个** __type **字段(注意:前缀是两个下划线)来标示出它们的类型,所以如果你采用正确的编码的话就可以读或者写这些字段。
Date 类型包含了一个 iso 字段,其值是一个 UTC 时间戳,以 ISO 8601 格式和毫秒级的精度来存储的时间值,格式为:YYYY-MM-DDTHH:MM:SS.MMMZ:
{
"__type": "Date",
"iso": "2015-06-21T18:02:52.249Z"
}
Date 和内置的 createdAt 字段和 updatedAt 字段相结合的时候特别有用,举个例子:为了找到在一个特殊时间发布的微博,只需要将 Date 编码后放在使用了比较条件的查询里面:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"createdAt":{"$gte":{"__type":"Date","iso":"2015-06-21T18:02:52.249Z"}}}' \
https://api.parse.cn/1.1/classes/Post
**Byte **类型包含了一个 base64 字段,这个字段是一些二进制数据编码过的 base64 字符串。base64 是 MIME 使用的标准,不包含空白符:
{
"__type": "Bytes",
"base64": "5b6I5aSa55So5oi36KGo56S65b6I5Zac5qyi5oiR5Lus55qE5paH5qGj6aOO5qC877yM5oiR5Lus5bey5bCGIExlYW5DbG91ZCDmiYDmnInmlofmoaPnmoQgTWFya2Rvd24g5qC85byP55qE5rqQ56CB5byA5pS+5Ye65p2l44CC"
}
**Pointer **类型是用来设定 AVObject 作为另一个对象的值时使用的,它包含了 className 和 objectId 两个属性值,用来提取目标对象:
{
"__type": "Pointer",
"className": "Post",
"objectId": "55a39634e4b0ed48f0c1845c"
}
指向用户对象的 Pointer 的 className 为** _User**,前面加一个下划线表示开发者不能定义的类名,而且所指的类是 Parse 平台内置的。
**Relation **类型被用在多对多的类型上,移动端使用 AVRelation 作为值,它有一个 className 字段表示目标对象的类名.
{
"__type": "Relation",
"className": "Post"
}
在进行查询时,Relation 对象的行为很像是 Pointer 的数组,任何针对于 pointer 数组的操作(include 除外)都可以对 Relation 起作用。
当更多的数据类型被加入的时候,它们都会采用 hashmap 加上一个 __type 字段的形式,所以你不应该使用 __type 作为你自己的 JSON 对象的 key。
查询
基础查询
通过发送一个 GET 请求到类的 URL 上,不需要任何 URL 参数,你就可以一次获取多个对象。下面就是简单地获取所有微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
https://api.Parse.cn/1.1/classes/Post
返回的值就是一个 JSON 对象包含了 results 字段,它的值就是对象的列表:
{
"results": [
{
"content": "近期 Parse 的文档已经支持评论功能,如果您有疑问、意见或是其他想法,都可以直接在我们文档中提出。",
"pubUser": "Parse官方客服",
"upvotes": 2,
"createdAt": "2015-06-29T03:43:35.931Z",
"objectId": "55a39634e4b0ed48f0c1845b"
},
{
"content": "每个 Java 程序员必备的 8 个开发工具",
"pubUser": "Parse官方客服",
"pubTimestamp": 123456789,
"createdAt": "2015-06-29T01:39:35.931Z",
"updatedAt": "2015-06-29T01:39:35.931Z",
"objectId": "558e20cbe4b060308e3eb36c"
}
]
}
注:应用控制台对** createdAt **和 **updatedAt **做了在展示优化,它们会依据用户操作系统时区而显示为本地时间;客户端 SDK 获取到这些时间后也会将其转换为本地时间;而通过 REST API 获取到的则是原始的 UTC 时间,开发者可能需要根据情况做相应的时区转换。
查询约束
通过** where **参数的形式可以对查询对象做出约束。
where 参数的值应该是 JSON 编码过的。就是说,如果你查看真正被发出的 URL 请求,它应该是先被 JSON 编码过,然后又被 URL 编码过。最简单的使用 where 参数的方式就是包含应有的 key 和 value。例如,如果我们想要看到「Parse官方客服」发布的所有微博,我们应该这样构造查询:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"pubUser":"LeanCloud官方客服"}' \
https://api.parse.cn/1.1/classes/Post
除了完全匹配一个给定的值以外,where 也支持比较的方式,而且它还支持对 key 的一些 hash 操作,比如包含。where 参数支持如下选项:
图2例如获取在 2015-06-29 当天发布的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"createdAt":{"$gte":{"__type":"Date","iso":"2015-06-29T00:00:00.000Z"},"$lt":{"__type":"Date","iso":"2015-06-30T00:00:00.000Z"}}}' \
https://api.parse.cn/1.1/classes/Post
获取标题以大写「WTO」开头的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"title":{"$regex":"^WTO.*","$options":"i"}}' \
https://api.parse.cn/1.1/classes/Post
求点赞次数少于 10 次,且该次数还是奇数的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"upvotes":{"$in":[1,3,5,7,9]}}' \
https://api.parse.cn/1.1/classes/Post
获取不是「Parse官方客服」发布的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"pubUser":{"$nin":["Parse官方客服"]}}' \
https://api.parse.cn/1.1/classes/Post
获取有人喜欢的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"upvotes":{"$exists":true}}' \
https://api.parse.cn/1.1/classes/Post
获取没有被人喜欢过的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"upvotes":{"$exists":false}}' \
https://api.parse.cn/1.1/classes/Post
微博有用户互相关注的功能,如果我们用 _Followee(用户关注的人) 和** _Follower(用户的粉丝) 这两个类来存储用户之间的关注关系(我们的 应用内社交组件 已经实现了这样的模型),我们可以创建一个查询来找到某个用户关注的人发布的微博(Post** 表中有一个字段 **author **指向发布者):
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-G \
--data-urlencode 'where={"author":{"$select":{"query":{"className":"_Followee","where":{"user":{
"__type": "Pointer",
"className": "_User",
"objectId": "55a39634e4b0ed48f0c1845c"
}}, "key":"followee"}}}}' \
https://api.parse.cn/1.1/classes/Post
**order **参数指定一个字段的排序方式,前面加一个负号表示逆序。返回 Post 记录并按发布时间升序排列:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=createdAt' \
https://api.parse.cn/1.1/classes/Post
降序排列:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=-createdAt' \
https://api.parse.cn/1.1/classes/Post
对多个字段进行排序,要使用逗号分隔的列表。将 Post 以 createdAt 升序和 pubUser 降序进行排序:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=createdAt,-pubUser' \
https://api.parse.cn/1.1/classes/Post
你可以用** limit **和 **skip 来做分页。limit **的默认值是 100,任何 1 到 1000 之间的值都是可选的,在 1 到 1000 范围之外的都强制转成默认的 100。比如为了获取排序在 400 到 600 之间的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'limit=200' \
--data-urlencode 'skip=400' \
https://api.parse.cn/1.1/classes/Post
你可以限定返回的字段通过传入** keys** 参数和一个逗号分隔列表。为了返回对象只包含** pubUser **和 **content **字段(还有特殊的内置字段比如 objectId、createdAt 和 updatedAt):
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'keys=pubUser,content' \
https://api.parse.cn/1.1/classes/Post
**keys **还支持反向选择,也就是不返回某些字段,字段名前面加个减号即可,比如我不想查询返回 author:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'keys=-author' \
https://api.parse.cn/1.1/classes/Post
所有以上这些参数都可以和其他的组合进行使用。
对数组的查询
如果 key 的值是数组类型,查找 key 值中有 2 的对象:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"arrayKey":2}' \
https://api.parse.cn/1.1/classes/TestObject
查找 key 值中有 2 或 3 或 4 的对象:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"arrayKey":{"$in":[2,3,4]}}' \
https://api.parse.cn/1.1/classes/TestObject
使用 $all 操作符来找到 key 值中同时有 2 和 3 和 4 的对象:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"arrayKey":{"$all":[2,3,4]}}' \
https://api.parse.cn/1.1/classes/TestObject
关系查询
有几种方式来查询对象之间的关系数据。如果你想获取对象,而这个对象的一个字段对应了另一个对象,你可以用一个 **where **查询,自己构造一个 Pointer,和其他数据类型一样。例如,每条微博都会有很多人评论,我们可以让每一个 Comment 将它对应的 Post 对象保存到 post 字段上,这样你可以取得一条微博下所有 Comment:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"post":{"__type":"Pointer","className":"Post","objectId":"558e20cbe4b060308e3eb36c"}}' \
https://api.parse.cn/1.1/classes/Comment
如果你想获取对象,这个对象的一个字段指向的对象需要另一个查询来指定,你可以使用 **$inQuery 操作符。注意 limit **的默认值是 100 且最大值是 1000,这个限制同样适用于内部的查询,所以对于较大的数据集你可能需要细心地构建查询来获得期望的结果。
如上面的例子,假设每条微博还有一个** image** 的字段,用来存储配图,你可以这样列出带图片的微博的评论数据:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"post":{"$inQuery":{"where":{"image":{"$exists":true}},"className":"Post"}}}' \
https://api.parse.cn/1.1/classes/Comment
如果你想获取作为其父对象的关系成员的对象,你可以使用 $relatedTo 操作符。例如对于微博这种社交类应用来讲,每一条微博都可以被不同的用户点赞,我们可以设计 Post 类下面有一个 key 是 Relation 类型,叫做 likes,存储了喜欢这个 Post 的所有** User。你可以通过下面的方式找到喜欢某条 Post 的所有用户(请注意,新创建应用的 ****_User**** 表的查询权限默认是关闭的,你可以通过 class 权限设置打开,请参考 ****数据与安全 - Class 级别的权限****。**):
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"$relatedTo":{"object":{"__type":"Pointer","className":"Post","objectId":"558e20cbe4b060308e3eb36c"},"key":"likes"}}' \
https://api.parse.cn/1.1/users
有时候,你可能需要在一个查询之中返回多种类型,你可以通过传入字段到 **include **参数中。比如,我们想获得最近的 10 篇评论,而你想同时得到它们关联的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=-createdAt' \
--data-urlencode 'limit=10' \
--data-urlencode 'include=post' \
https://api.parse.cn/1.1/classes/Comment
不是作为一个 Pointer 表示,post 字段现在已经被展开为一个完整的对象:__type 被设置为 Object 而** className **同样也被提供了。例如,一个指向 Post 的 Pointer 可能被展示为:
{
"__type": "Pointer",
"className": "Post",
"objectId": "51e3a359e4b015ead4d95ddc"
}
当一个查询使用** include **参数来包含进去来取代 pointer 之后,可以看到 pointer 被展开为:
{
"__type": "Object",
"className": "Post",
"objectId": "51e3a359e4b015ead4d95ddc",
"createdAt": "2015-06-29T09:31:20.371Z",
"updatedAt": "2015-06-29T09:31:20.371Z",
"desc": "Post 的其他字段也会一同被包含进来。"
}
你可以同样做多层的** include,这时要使用点号(.)。如果你要 include 一个 Comment 对应的 Post 对应的 author**:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'order=-createdAt' \
--data-urlencode 'limit=10' \
--data-urlencode 'include=post.author' \
https://api.parse.cn/1.1/classes/Comment
如果你要构建一个查询,这个查询要 include 多个类,此时用逗号分隔列表即可。
对象计数
如果你在使用 limit,或者如果返回的结果很多,你可能想要知道到底有多少对象应该返回,而不用把它们全部获得以后再计数,此时你可以使用 count 参数。举个例子,如果你仅仅是关心一个某个用户发布了多少条微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"pubUser":"Parse官方客服"}' \
--data-urlencode 'count=1' \
--data-urlencode 'limit=0' \
https://api.parse.cn/1.1/classes/Post
因为这个 request 请求了 count 而且把** limit 设为了 0,返回的值里面只有计数,没有 results**:
{
"results": [
],
"count": 7
}
如果有一个非 0 的** limit **的话,则既会返回 results 也会返回 count。
复合查询
$or 操作符用于查询符合任意一种条件的对象,它的值为一个 JSON 数组。例如,查询企业账号和个人账号的微博:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'where={"$or":[{"pubUserCertificate":{"$gt":2}},{"pubUserCertificate":{"$lt":3}}]}' \
https://api.parse.cn/1.1/classes/Post
任何在查询上的其他约束都会对返回的对象生效,所以你可以用** $or **对其他的查询添加约束。
注意我们不会在组合查询的子查询中支持非过滤型的约束(例如 limit、skip、order、include)。
使用 CQL 查询
我们还提供类 SQL 语法的 CQL 查询语言,查询应用内数据,例如:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'cql=select * from Post limit 0,100 order by pubUser' \
https://api.parse.cn/1.1/cloudQuery
(更多请参考 CQL 详细指南。)
CQL 还支持占位符查询,where 和 limit 子句的条件参数可以使用问号替换,然后通过 pvalues 数组传入:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'cql=select * from Post where pubUser=? limit ?,? order by createdAt' \
--data-urlencode 'pvalues=["dennis", 0, 100]'
https://api.parse.cn/1.1/cloudQuery
子查询的局限
使用子查询可能会遇到查不到记录或查到的记录不全的情况。例如:
-- 找出积分高于 80、region 为 cn 的玩家记录
SELECT *
FROM player
WHERE NAME IN (SELECT NAME
FROM gamescore
WHERE score > 80)
AND region = 'cn'
Parse云端使用的并非关系型数据库,无法做到真正的联表查询,所以实际的处理方式是:先执行内嵌/子查询(和普通查询一样,limit 默认为 100,最大 1000),然后将子查询的结果填入主查询的对应位置,再执行主查询。
如果子查询匹配到了 100 条以上的记录(性别等区分度低的字段重复值往往较多),且主查询有其他查询条件(region = 'cn'),那么可能会出现没有结果或结果不全的情况,其本质上是子查询查出的 100 条记录没有满足主查询的其他条件。
我们建议采用以下方案进行改进:
- 确保子查询的结果在 100 条以下,如果在 100 - 1000 条的话请在子查询末尾添加 limit 1000。
- 将需要查询的字段冗余到主查询所在的表上;例如将 score 冗余到 Player 表上,或者将 region 添加到 GameScore 上然后只查 GameScore 表。
- 进行多次查询,每次在子查询上添加 skip 来遍历所有记录(注意 skip 的值较大时可能会引发性能问题,因此不是很推荐)。
用户
不仅在移动应用上,还在其他系统中,很多应用都有一个统一的登录流程。通过 REST API 访问用户的账户让你可以在 Parse 上简单实现这一功能。
通常来说,用户(类名** _User**)这个类的功能与其他的对象是相同的,比如都没有限制模式(Schema free)。User 对象和其他对象不同的是一个用户必须有用户名(username)和密码(password),密码会被自动地加密和存储。Parse 强制要求 username 和 email 这两个字段必须是没有重复的。
注册
注册一个新用户与创建一个新的普通对象之间的不同点在于 username 和 password 字段都是必需的。password 字段会以和其他的字段不一样的方式处理,它在储存时会被加密而且永远不会被返回给任何来自客户端的请求。
你可以让 Parse 自动验证邮件地址,做法是进入 控制台 > 设置 > 应用选项,勾选 用户账号 下的 用户注册时,发送验证邮件。
这项设置启用了的话,所有填写了 email 的用户在注册时都会产生一个 email 验证地址,并发回到用户邮箱,用户打开邮箱点击了验证链接之后,用户表里** emailVerified 属性值会被设为 true。你可以在 emailVerified** 字段上查看用户的 email 是否已经通过验证。
为了注册一个新的用户,需要向 user 路径发送一个 POST 请求,你可以加入一个新的字段,例如,创建一个新的用户有一个电话号码:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"username":"hjiang","password":"f32@ds*@&dsa","phone":"12312340000"}' \
https://api.parse.cn/1.1/users
当创建成功时,HTTP返回为 201 Created,Location 头包含了新用户的 URL:
Status: 201 Created
Location: https://api.leancloud.cn/1.1/users/55a47496e4b05001a7732c5f
返回的主体是一个 JSON 对象,包含 objectId、createdAt 时间戳表示创建对象时间,sessionToken 可以被用来认证这名用户随后的请求:
{
"sessionToken":"qmdj8pdidnmyzp0c7yqil91oc",
"createdAt":"2015-07-14T02:31:50.100Z",
"objectId":"55a47496e4b05001a7732c5f"
}
登录
在你允许用户注册之后,在以后你需要让他们用自己的用户名和密码登录。为了做到这一点,发送一个 POST 请求到 /1.1/login,加上 username 和 password 作为 body。
curl -X POST \
-H "Content-Type: application/json" \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-d '{"username":"hjiang","password":"f32@ds*@&dsa"}' \
https://api.parse.cn/1.1/login
返回的主体是一个 JSON 对象包括所有除了 password 以外的自定义字段。它同样包含了 createdAt、updateAt、objectId 和 sessionToken 字段。
{
"sessionToken":"qmdj8pdidnmyzp0c7yqil91oc",
"updatedAt":"2015-07-14T02:31:50.100Z",
"phone":"12312340000",
"objectId":"55a47496e4b05001a7732c5f",
"username":"hjiang",
"createdAt":"2015-07-14T02:31:50.100Z",
"emailVerified":false,
"mobilePhoneVerified":false
}
可以将 sessionToken 理解为用户的登录凭证,每个用户的 sessionToken 在同一个应用内都是唯一的, 类似于 Cookie 的概念。
正常情况下,用户的 sessionToken 是固定不变的,但在以下情况下会发生改变:
- 用户调用了忘记密码功能,重设了密码。
- 用户在控制台的 应用选项 中勾选了 密码修改后,强制客户端重新登录,那么在修改密码后 sessionToken 也将强制更换。
- 调用 refreshSessionToken 主动重置。
在 sessionToken 变化后,已有的登录如果调用到用户相关权限受限的 API,将返回 403 权限错误。
已登录的用户信息
用户成功注册或登录后,服务器会返回 sessionToken 并保存在本地,后续请求可以通过传递 sessionToken 来获取该用户信息(如访问权限等)。更多说明请参考 存储 · sessionToken。
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://api.parse.cn/1.1/users/me
返回的 JSON 数据与 /login 登录请求所返回的相同。
重置登录 sessionToken
可以主动重置用户的 sessionToken:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://api.parse.cn/1.1/users/57e3bcca67f35600577c3063/refreshSessionToken
调用这个 API 要求传入登录返回的 X-LC-Session 作为认证,或者使用 Master Key。
重置成功将返回新的 sessionToken 及用户信息:
{
"sessionToken":"5frlikqlwzx1nh3wzsdtfr4q7",
"updatedAt":"2016-10-20T03:10:57.926Z",
"objectId":"57e3bcca67f35600577c3063",
"username":"parse",
"createdAt":"2016-09-22T11:13:14.842Z",
"emailVerified":false,
"mobilePhoneVerified":false
}
账户锁定
输入错误的密码或验证码会导致用户登录失败。如果在 15 分钟内,同一个用户登录失败的次数大于 6 次,该用户账户即被云端暂时锁定,此时云端会返回错误码* {"code":219,"error":"登录失败次数超过限制,请稍候再试,或者通过忘记密码重设密码。"}*,开发者可在客户端进行必要提示。
锁定将在最后一次错误登录的 15 分钟之后由云端自动解除,开发者无法通过 SDK 或 REST API 进行干预。在锁定期间,即使用户输入了正确的验证信息也不允许登录。这个限制在 SDK 和云引擎中都有效。
使用手机号码注册或登录
请参考 短信服务 REST API 详解 · 使用手机号码注册或登录。
验证 Email
设置 email 验证是 app 设置中的一个选项,通过这个标识,应用层可以对提供真实 email 的用户更好的功能或者体验。Email 验证会在 User 对象中加入 *emailVerified 字段,当一个用户的 email 被新设置或者修改过的话,emailVerified 会被重置为 false。Parse后台会往用户填写的邮箱发送一个验证链接,用户点击这个链接可以让 emailVerified *被设置为 true。
emailVerified 字段有 3 种状态可以参考:
- true:用户已经点击了发送到邮箱的验证地址,邮箱被验证为真实有效。LeanCloud 保证在新创建用户的时候 emailVerified 一定为 false。
- false:User 对象最后一次被更新的时候,用户并没有确认过他的 email 地址。如果你看到 emailVerified 为 false 的话,你可以考虑刷新 User 对象或者再次请求验证用户邮箱。
- null:User对象在 email 验证没有打开的时候就已经创建了,或者 User 没有 email。
关于自定义邮件模板和验证链接请看博客文章《自定义应用内用户重设密码和邮箱验证页面》。
请求验证 Email
发送给用户的邮箱验证邮件在一周内失效,你可以通过调用 */1.1/requestEmailVerify *来强制重新发送:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"email":"hang@leancloud.rocks"}' \
https://api.parse.cn/1.1/requestEmailVerify
请求密码重设
在用户将 email 与他们的账户关联起来之后,你可以通过邮件来重设密码。操作方法为,发送一个 POST 请求到 /1.1/requestPasswordReset,同时在 request 的 body 部分带上 email 字段。
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{"email":"hang@leancloud.rocks"}' \
https://api.parse.cn/1.1/requestPasswordReset
如果成功的话,返回的值是一个 JSON 对象。
关于自定义邮件模板和验证链接请看这篇博客文章《自定义应用内用户重设密码和邮箱验证页面》。
手机号码验证
请参考 短信服务 REST API 详解 - 用户账户与手机号码验证。
获取用户
你可以发送一个 GET 请求到 URL 以获取用户的账户信息,返回的内容就是当创建用户时返回的内容。比如,为了获取上面创建的用户:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://api.parse.cn/1.1/users/55a47496e4b05001a7732c5f
返回的 body 是一个 JSON 对象,包含所有用户提供的字段,除了密码以外,也包括了 createdAt、 updatedAt 和 objectId 字段.
{
"updatedAt":"2015-07-14T02:31:50.100Z",
"phone":"18612340000",
"objectId":"55a47496e4b05001a7732c5f",
"username":"hjiang",
"createdAt":"2015-07-14T02:31:50.100Z",
"emailVerified":false,
"mobilePhoneVerified":false
}
更新用户
在通常的情况下,没有人会允许别人来改动他们自己的数据。为了做好权限认证,确保只有用户自己可以修改个人数据,在更新用户信息的时候,必须在 HTTP 头部加入一个 *X-LC-Session *项来请求更新,这个 session token 在注册和登录时会返回。
为了改动一个用户已经有的数据,需要对这个用户的 URL 发送一个 PUT 请求。任何你没有指定的 key 都会保持不动,所以你可以只改动用户数据中的一部分。username 和 password 也是可以改动的,但是新的 username 不能和既有数据重复。
比如,如果我们想对 「hjiang」 的手机号码做出一些改动:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
-H "Content-Type: application/json" \
-d '{"phone":"12300001234"}' \
https://api.parse.cn/1.1/users/55a47496e4b05001a7732c5f
返回的 body 是一个 JSON 对象,只有一个** updatedAt **字段表明更新发生的时间.
{
"updatedAt": "2015-07-14T02:35:50.100Z"
}
安全地修改用户密码
修改密码,可以直接使用上面的PUT /1.1/users/:objectId的 API,但是很多开发者会希望让用户输入一次旧密码做一次认证,旧密码正确才可以修改为新密码,因此我们提供了一个单独的** API PUT** /1.1/users/:objectId/updatePassword 来安全地更新密码:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
-H "Content-Type: application/json" \
-d '{"old_password":"the_old_password", "new_password":"the_new_password"}' \
https://api.parse.cn/1.1/users/55a47496e4b05001a7732c5f/updatePassword
- old_password**:用户的老密码
- new_password**:用户的新密码
注意:仍然需要传入 X-LC-Session,也就是登录用户才可以修改自己的密码。
查询
请注意,新创建应用的 _User 表的查询权限默认是关闭的,你可以通过 class 权限设置打开,请参考 数据与安全 - Class 级别的权限。
你可以一次获取多个用户,只要向用户的根 URL 发送一个 GET 请求。没有任何 URL 参数的话,可以简单地列出所有用户:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://api.parse.cn/1.1/users
返回的值是一个 JSON 对象包括一个 **results **字段,值是包含了所有对象的一个 JSON 数组。
{
"results":[
{
"updatedAt":"2015-07-14T02:31:50.100Z",
"phone":"12312340000",
"objectId":"55a47496e4b05001a7732c5f",
"username":"hjiang",
"createdAt":"2015-07-14T02:31:50.100Z",
"emailVerified":false,
"mobilePhoneVerified":false
}
]
}
所有的对普通对象的查询选项都适用于对用户对象的查询,所以可以查看 查询 部分来获取详细信息。
删除用户
为了在 Parse上删除一个用户,可以向它的 URL 上发送一个 DELETE 请求。同样的,你必须提供一个 X-LC-Session 在 HTTP 头上以便认证。例如:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://api.parse.cn/1.1/users/55a47496e4b05001a7732c5f
连接用户账户和第三方平台
Parse 允许你连接你的用户到其他服务,比如新浪微博和腾讯微博,这样就允许你的用户直接用他们现有的账号 id 来登录你的 App。通过 **signup **或者更新的 endpoint,并使用 **authData 字段来提供你希望连接的服务的授权信息就可以做到。一旦关联了某个服务,authData **将被存储到你的用户信息里。如需重新获取该内容,请参考 获取 authData。
authData 是一个普通的 JSON 对象,它所要求的 key 根据 service 不同而不同,具体要求见下面。每种情况下,你都需要自己负责完成整个授权过程(一般是通过 OAuth 协议,1.0 或者 2.0) 来获取授权信息,提供给连接 API。
新浪微博 的 authData 内容:
{
"authData": {
"weibo": {
"uid": "123456789",
"access_token": "2.00vs3XtCI5FevCff4981adb5jj1lXE",
"expiration_in": "36000"
}
}
}
腾讯微博 的 authData 内容:
{
"authData": {
"qq": {
"openid": "0395BA18A5CD6255E5BA185E7BEBA242",
"access_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
"expires_in": 1382686496
}
}
}
微信 的 authData 内容:
{
"authData": {
"weixin": {
"openid": "0395BA18A5CD6255E5BA185E7BEBA242",
"access_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
"expires_in": 1382686496
}
}
}
匿名用户(Anonymous user)的 authData 内容:
{
"anonymous": {
"id": "random UUID with lowercase hexadecimal digits"
}
}
+
其他任意第三方平台(其他第三方将不支持校验 access token 选项):
+
{
"第三方平台名称,例如facebook":
{
"uid": "在第三方平台上的唯一用户id字符串",
"access_token": "在第三方平台的 access token",
……其他可选属性
}
}
要保证一个第三方账号只绑定到一个 LeanCloud 应用内用户上,就需要为 _User 表中的 authData.第三方平台名称.uid 建立唯一索引;另外当 **_User **表的记录数量超过了 1 万条,用户也无法自行创建该索引。这两种操作都需要提交工单或联系 support@leancloud.rocks 来创建。
获取 authData
authData 的数据默认为不可查询,也就是说它既不包含在当前登录用户的数据中,也无法在 query 中以 include 或 fetch 来获取。要更改这一默认行为,需要进入 应用控制台 > 存储 > _User 表,点击 authData 字段右侧的箭头,选择 编辑,关掉「列属性」里的「客户端不可见」选项才能获取到。
注册和登录
使用一个连接服务来注册用户并登录,同样使用 POST 请求 users,只是需要提供 **authData **字段。例如,使用新浪微博账户注册或者登录用户:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"authData": {
"qq": {
"openid": "0395BA18A5CD6255E5BA185E7BEBA242",
"access_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
"expires_in": 1382686496
}
}
}' \
https://api.parse.cn/1.1/users
Parse会校验提供的 **authData **是否有效,并检查是否已经有一个用户连接了这个 authData 服务。如果已经有用户存在并连接了同一个 authData,那么返回 200 OK 和详细信息(包括用户的 sessionToken):
Status: 200 OK
Location: https://api.parse.cn/1.1/users/75a4800fe4b05001a7745c41
应答的 body 类似:
{
"username": "Parse",
"createdAt": "2015-06-28T23:49:36.353Z",
"updatedAt": "2015-06-28T23:49:36.353Z",
"objectId": "75a4800fe4b05001a7745c41",
"sessionToken": "anythingstringforsessiontoken",
"authData": {
"qq": {
"openid": "0395BA18A5CD6255E5BA185E7BEBA242",
"access_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
"expires_in": 1382686496
}
}
}
如果用户还没有连接到这个账号,则你会收到 201 Created 的应答状态码,标识新的用户已经被创建:
Status: 201 Created
Location: https://api.leancloud.cn/1.1/users/55a4800fe4b05001a7745c41
应答内容包括 objectId、createdAt、sessionToken 以及一个自动生成的随机 username,例如:
{
"username":"ec9m07bo32cko6soqtvn6bko5",
"sessionToken":"tfrvbzmdf609nu9204v5f0tuj",
"createdAt":"2015-07-14T03:20:47.733Z",
"objectId":"55a4800fe4b05001a7745c41"
}
连接
连接一个现有的用户到新浪微博或者腾讯微博账号,可以向 user endpoint 发送一个附带 **authData **字段的 PUT 请求来实现。例如,连接一个用户到新浪微博账号发起的请求类似这样:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
-H "Content-Type: application/json" \
-d '{
"authData": {
"weibo": {
"uid": "123456789",
"access_token": "2.00vs3XtCI5FevCff4981adb5jj1lXE",
"expiration_in": "36000"
}
}
}' \
https://api.parse.cn/1.1/users/55a47496e4b05001a7732c5f
完成连接后,你可以使用匹配的 **authData **来认证他们。
断开连接
断开一个现有用户到某个服务,可以发送一个PUT请求设置 ##authData## 中对应的服务为 null 来做到。例如,取消新浪微博关联:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
-H "Content-Type: application/json" \
-d '{
"authData": {
"weibo" : null
}
}' \
https://api.parse.cn/1.1/users/55a47496e4b05001a7732c5f
安全
当你用 REST API key 来访问 Parse时,访问可能被## ACL ##所限制,就像 iOS 和 Android SDK 上所做的一样。你仍然可以通过 REST API 来读和修改,只需要通过 ACL 的 key 来访问一个对象。
ACL 按 JSON 对象格式来表示,JSON 对象的 key 是 objectId 或者一个特别的 key(*,表示公共访问权限)。ACL 的值是权限对象,这个 JSON 对象的 key 即是权限名,而这些 key 的值总是 true。
举个例子,如果你想让一个 id 为 55a47496e4b05001a7732c5f 的用户有读和写一个对象的权限,而且这个对象应该可以被公共读取,符合的 ACL 应该是:
{
"55a47496e4b05001a7732c5f": {
"read": true,
"write": true
},
"*": {
"read": true
}
}
角色
当你的 app 的规模和用户基数成长时,你可能发现你需要比 ACL 模型(针对每个用户)更加粗粒度的访问控制你的数据的方法。为了适应这种需求,Parse支持一种基于角色的权限控制方式。角色系统提供一种逻辑方法让你通过权限的方式来访问你的数据,角色是一种有名称的对象,包含了用户和其他角色。任何授予一个角色的权限隐含着授予它包含着的其他的角色相应的权限。
例如,在你的 app 中管理着一些内容,你可能有一些类似于「主持人」的角色可以修改和删除其他用户创建的新的内容,你可能还有一些「管理员」有着与「主持人」相同的权限,但是还可以修改 app 的其他全局性设置。通过给予用户这些角色,你可以保证新的用户可以做主持人或者管理员,不需要手动地授予每个资源的权限给各个用户。
我们提供一个特殊的角色(Role)类来表示这些用户组,为了设置权限用。角色有一些和其他对象不太一样的特殊字段。
图3通常来说,为了保持这些角色安全,你的移动 app 不应该为角色的创建和管理负责。作为替代,角色应该是通过一个不同的网页上的界面来管理,或者手工被管理员所管理。我们的 REST API 允许你不需要一个移动设备就能管理你的角色。
创建角色
创建一个新的角色与其他的对象不同的是 name 字段是必须的。角色必须指定一个 ACL,这个 ACL 必须尽量的约束严格一些,这样可以防止错误的用户修改角色。
创建一个新角色,发送一个 POST 请求到 roles 根路径:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"name": "Manager",
"ACL": {
"*": {
"read": true
}
}
}' \
https://api.parse.cn/1.1/roles
其返回值类似于:
{
"createdAt":"2015-07-14T03:34:41.074Z",
"objectId":"55a48351e4b05001a774a89f"
}
你可以通过加入已有的对象到 roles 和 users 关系中来创建一个有子角色和用户的角色:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"name": "CLevel",
"ACL": {
"*": {
"read": true
}
},
"roles": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_Role",
"objectId": "55a48351e4b05001a774a89f"
}
]
},
"users": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_User",
"objectId": "55a47496e4b05001a7732c5f"
}
]
}
}' \
https://api.parse.cn/1.1/roles
当创建成功时,HTTP 返回是 201 Created 而 Location header 包含了新的对象的 URL:
Status: 201 Created
Location: https://api.leancloud.cn/1.1/roles/55a483f0e4b05001a774b837
获取角色
你可以同样通过发送一个 GET 请求到 Location header 中返回的 URL 来获取这个对象,比如我们想要获取上面创建的对象:
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://api.parse.cn/1.1/roles/55a483f0e4b05001a774b837
响应的 body 是一个 JSON 对象包含角色的所有字段:
{
"name":"CLevel",
"createdAt":"2015-07-14T03:37:20.992Z",
"updatedAt":"2015-07-14T03:37:20.994Z",
"objectId":"55a483f0e4b05001a774b837",
"users":{
"__type":"Relation",
"className":"_User"
},
"roles":{
"__type":"Relation",
"className":"_Role"
}
}
注意 users 和 roles 关系无法在 JSON 中见到,你需要相应地用 $relatedTo 操作符来查询角色中的子角色和用户。
更新角色
更新一个角色通常可以像更新其他对象一样使用,但是 name 字段是不可以更改的。加入和删除 users 和 roles 可以通过使用**AddRelation **和 RemoveRelation操作来进行。
举例来说,我们对 Manager 角色加入 1 个用户:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"users": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_User",
"objectId": "55a4800fe4b05001a7745c41"
}
]
}
}' \
https://api.parse.cn/1.1/roles/55a48351e4b05001a774a89f
相似的,我们可以删除一个 Manager 的子角色:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"roles": {
"__op": "RemoveRelation",
"objects": [
{
"__type": "Pointer",
"className": "_Role",
"objectId": "55a483f0e4b05001a774b837"
}
]
}
}' \
https://api.parse.cn/1.1/roles/55a48351e4b05001a774a89f
删除对象
为了从 Parse 上删除一个角色,只需要发送 DELETE 请求到它的 URL 就可以了。
我们需要传入 X-LC-Session 来通过一个有权限的用户账号来访问这个角色对象,例如:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "X-LC-Session: qmdj8pdidnmyzp0c7yqil91oc" \
https://api.parse.cn/1.1/roles/55a483f0e4b05001a774b837
安全性
当你通过 REST API key 访问 Parse 的时候,访问同样可能被 ACL 所限制,就像 iOS 和 Android SDK 上一样。你仍然可以通过 REST API 来读和修改 ACL,只用通过访问「ACL」键就可以了。
除了用户级的权限设置以外,你可以通过设置角色级的权限来限制对 Parse对象的访问。取代了指定一个 objectId 带一个权限的方式,你可以设定一个角色的权限为它的名字在前面加上** role**: 前缀作为 key。你可以同时使用用户级的权限和角色级的权限来提供精细的用户访问控制。
比如,为了限制一个对象可以被在 Staff 里的任何人读到,而且可以被它的创建者和任何有 Manager 角色的人所修改,你应该向下面这样设置 ACL:
{
"55a4800fe4b05001a7745c41": {
"write": true
},
"role:Staff": {
"read": true
},
"role:Manager": {
"write": true
}
}
你不必为创建的用户和 Manager 指定读的权限,如果这个用户和 Manager 本身就是 Staff 的子角色和用户,因为它们都会继承授予 Staff 的权限。
角色继承
就像上面所说的一样,一个角色可以包含另一个,可以为 2 个角色建立一个「父子」关系。这个关系的结果就是任何被授予父角色的权限隐含地被授予子角色。
这样的关系类型通常在用户管理的内容类的 app 上比较常见,比如论坛。有一些少数的用户是「管理员」,有最高级的权限来调整程序的设置、创建新的论坛、设定全局的消息等等。
另一类用户是「版主」,他们有责任保证用户生成的内容是合适的。任何有管理员权限的人都应该有版主的权利。为了建立这个关系,你应该把「Administartors」的角色设置为「Moderators」 的子角色,具体来说就是把 Administrators 这个角色加入 Moderators 对象的 roles 关系之中:
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"roles": {
"__op": "AddRelation",
"objects": [
{
"__type": "Pointer",
"className": "_Role",
"objectId": "<AdministratorsRoleObjectId>"
}
]
}
}' \
文件
对于文件上传,我们推荐使用各个客户端的 SDK 进行操作,或者使用命令行工具。
通过** REST API **上传文件受到三个限制:
- 上传最大文件大小有 10 M 的限制
- 每个应用每秒最多上传 1 个文件
- 每个应用每分钟最多上传 30 个文件
而使用 SDK 或者命令行上传没有这些限制。
上传文件
上传文件到 Parse通过 POST 请求,注意必须指定文件的 content-type,例如上传一个文本文件 hello.txt 包含一行字符串:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: text/plain" \
-d 'Hello, World!' \
https://api.parse.cn/1.1/files/hello.txt
文件上传成功后,返回 201 Created 的应答和创建的文件对象(可以在 _File 表看到):
{ "size":13,
"bucket":"1qdney6b",
"url":"http://ac-1qdney6b.qiniudn.com/3zLG4o0d27MsCQ0qHGRg4JUKbaXU2fiE35HdhC8j.txt",
"name":"hello.txt",
"createdAt":"2014-10-14T05:55:57.455Z",
"objectId":"543cbaede4b07db196f50f3c"
}
其中** url** 就是文件下载链接,objectId 是文件的对象 id,name 就是上传的文件名称。
也可以尝试上传一张图片,假设当前目录有一个文件 test.png:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: image/png" \
--data-binary '@test.png' \
https://api.parse.cn/1.1/files/test.png
关联文件到对象
一个文件上传后,你可以关联该文件到某个 AVObject 对象上:
curl -X POST \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d '{
"name": "hjiang",
"picture": {
"id": "543cbaede4b07db196f50f3c",
"__type": "File"
}
}' \
https://api.parse.cn/1.1/classes/Staff
其中 id 就是文件对象的 objectId
删除文件
知道文件对象 ObjectId 的情况下,可以通过 DELETE 删除文件:
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
https://api.parse.cn/1.1/files/543cbaede4b07db196f50f3c