MAS · JavaScript 使用指南
1. 简介
MAS(MIS Application Service)提供了快速开发MIS的多项功能,旨在开发MIS系统时减少与后端的联调沟通,让前端能够轻松HOLD住一个完整MIS的开发。下面我们就MAS最重要的数据存储功能与传统数据库的对比来简要介绍一下MAS的特点。
在传统数据库中,当我们要进行向Todo表中一条数据的增加,我们会这样做:
INSERT INTO Todo (title,content) VALUES ('周会','周二下午2点整');
那么当我们使用MAS的数据存储功能时,实现代码如下:
var todo = MASObject;todo;todo;todo
使用MAS的特点在于:
- 不需要单独的维护表结构。比如需要新增字段你只需要这样改动代码
var todo = MASObject;todo;todo;todo;todo
- Schema Free,数据可以随用随加
- 能够提供一套统一的SDK,给予不同语言和不同环境的支持
MAS与传统数据库的区别在于:
- Schema Free/Not free 的差异;
- 数据接口上,MAS 是面向对象的(数据操作接口都是基于 Object 的),开放的(所有移动端都可以直接访问),DB 是面向结构的,封闭的(一般在 Server 内部访问)
目前,MAS针对MIS开发的特点集成了常用的功能:
接下来我们会一一介绍各个功能的使用
2. SDK安装
对于浏览器环境,只需要引入对应的sdk即可
对于node环境来说,你可以使用npm进行安装
npm install mas-js-sdk --save
之后我们就可以进行MAS的使用了
// 浏览器环境var appId = 'your appId'var appKey = 'your appKey'windowMAS = appId appKey ; // node环境var appId = 'your appId'var appKey = 'your appKey'req__MAS__ = appId appKey ;
3. 安全验证
MAS的安全验证有一套严格的流程,通过这个流程我们可以将数据权限细化到一个数据的读写,其流程步骤为:
- 对app的访问权限验证(通过应用创建的appId及appKey进行验证)
- 对用户进行权限验证 (通过MAS.User对象login后获得的uid和token进行验证)
- 对CRUD的Class进行权限验证(判断用户是否在Class的CRUD相关权限列表中)
- 对数据的read和write进行权限验证(通过创建数据时的ACL进行验证)
4. 数据存储
4.1 对象
MAS.Object是MAS对数据存储过程的复杂封装,每个MAS.Object的实例包含了诸多的键值对(key-value)。属性的值严格与JSON方式兼容的数据。当数据进行保存时,MAS.Object会对数据进行JSON.stringify。这个数据是无模式化的(Schema Free),这意味着你不需要提前标注每个对象有哪些key,你只需要随意的添加它就好了,服务器会按照相关逻辑保存它(注意:如果在MAS平台上,没有对添加字段进行相关Class列的添加,那么在查询时会被自动的过滤掉)。
4.1.1 数据类型
MAS.Object支持部分标准的JS数据类型,如下:
var todo = MASObject; var number = 2014;var string = 'famous film name is ' + number;var date = ;var array = string number;var object = number: number string: string ; todo;todo;todo;todo;todo;todo;todo;
注意,MAS.Object不能存储二进制数据(例如Blob相关),如果要对Blob数据进行存储,那么请使用 MAS.File。
4.1.2 创建对象
创建对象可以使用两种方式:
// 1.创建Class后再进行实例化var Todo = MASObject;var todo = ; // 2.直接实例化var todo = MASObject;
但需要注意的是,不管是extend还是new,对应的参数都应该准确对应你创建应用的Class名称。
4.1.3 保存对象
我们假设已经创建了一个叫做Todo的Class,并且其包含了title、content、location三个自定义列,那么当需要新建一条数据时对应的代码如下:
var todo = MASObject;todo;todo;todo;todo;
为提高代码的可读性,我们建议使用驼峰式命名法(CamelCase)为Class及属性进行命名。类使用大驼峰方式,如UserDetail,属性使用小驼峰,如updatedAt。
此外,在保存对象时我们可以进行fetchWhenSave的设定,fetchWhenSave用于对象成功保存后,自动返回本地已改动属性在云端的最新值,而不是本地save的数据,其默认为false,我们会在更新数据时讲解它的使用场景。
4.1.4 获取对象
每个被保存在服务端的数据都会有一个objectId标示,我们可以通过objectId获得对应的数据:
var query = 'Todo'; query;
如果不想使用查询,还可以通过从本地构建一个 id,然后调用接口从云端把这个 id 的数据拉取到本地,示例代码如下:
var todo = MASObject; todo;// ortodo; todo;
4.1.5 获取objectId
每一次对象存储成功之后,云端都会返回 objectId,它是一个Class中全局唯一的属性。
var todo = MASObject;todo;todo;todo;todo;
4.1.6 访问对象属性
访问Todo的对象属性的方法为:
var todo = MASObject;todo;todo;
如果访问了并不存在的属性,SDK 并不会抛出异常,而是会返回空值。
4.1.7 默认属性
MAS创建Class会有对应的默认属性,它包括了objectId、createdUid、updatedUid、createdAt、updatedAt。
- objectId:Class中数据的全局唯一标示,相当于关系型数据库中的主键。
- createdUid:创建当条数据的用户Id
- updatedUid:修改数据的用户Id
- createdAt:创建数据的时间,Unix时间戳
- updatedAt:修改数据的时间,Unix时间戳
4.1.8 同步对象
多终端共享一个数据时,为了确保当前客户端拿到的对象数据是最新的,可以调用刷新接口来确保本地数据与云端的同步:
// 使用已知 objectId 构建一个 MAS.Object var todo = ; todo; todo;
4.1.9 更新对象
MAS 上的更新对象都是针对单个对象,云端会根据 有没有 objectId 来决定是新增还是更新一个对象。
// 使用已知 objectId 构建一个 MAS.Objectvar todo = ;todo;todo;
更新操作是覆盖式的,云端会根据最后一次提交到服务器的有效请求来更新数据。更新是字段级别的操作,未更新的字段不会产生变动,这一点请不用担心。
由于更新会根据最后一次提交到服务器的请求来判断(乐观锁机制),因此为了保证在多人同时修改同一条数据时,你可以使用fetchWhenSave保证数据与服务端的同步,MAS.Object的fetchWhenSave默认为false。
考虑这样一个场景:一篇 wiki 文章允许任何人来修改,它的数据表字段有:content(wiki 内容)、version(版本号)。每当 wiki 内容被更新后,其 version 也需要更新(+1)。用户 A 要修改这篇 wiki,从数据表中取出时其 version 值为 3,当用户 A 完成编辑要保存新内容时,如果数据表中的 version 仍为 3,表明这段时间没有其他用户更新过这篇 wiki,可以放心保存;如果不是 3而是更高的值,那么此次修改应该被丢弃,当设置了fetchWhenSave为true时,客户端将会得到最新的修改值,保证了数据的同步(fetchWhenSave的依据逻辑为updatedAt字段)。
'Wiki';
4.1.10 数值更新
对Number数值的更新MAS提供了increment方法
var todo = MASObject; todo; todo; todo;
4.1.11 更新数组
更新数组是原子操作。使用以下方法可以方便地维护数组类型的数据:
- MAS.Object.prototype.add(attrKey, value) 将指定对象附加到数组末尾。
- MAS.Object.addUnique(attrKey, value) 如果数组中不包含指定对象,将该对象加入数组末尾
- MAS.Object.remove(attrKey, value) 从数组字段中删除指定对象的所有实例
例如,Todo 对象有一个提醒时间 reminders 字段,是一个数组,代表这个日程会在哪些时间点提醒用户。比如有个拖延症患者把闹钟设为早上的 7:10、7:20、7:30:
var reminder1 = +'2015-11-11 07:10:00'; var reminder2 = +'2015-11-11 07:20:00'; var reminder3 = +'2015-11-11 07:30:00'; var reminders = reminder1 reminder2 reminder3; var todo = MASObject; // 指定 reminders 是一个 Unix时间戳 对象数组 todo; todo;
4.1.12 删除对象
假如某一个 Todo 完成了,用户想要删除这个 Todo 对象,可以如下操作:
var todo = MASObject;todo;todo;
删除对象是一个较为敏感的操作。在控制台创建对象的时候,请认真考虑Class对应的权限设置,对于数据的删除,我们推荐定义一个字段isDeleted,依靠isDeleted的值来判断数据是否被删除的方式。
4.1.12 批量操作
为了减少网络交互的次数太多带来的时间浪费,你可以在一个请求中对多个对象进行创建、更新、删除、获取。接口都在 MAS.Object 这个类下面:
var objects = ; // 构建一个本地的 MAS.Object 对象数组 // 批量创建(更新)MASObject; // 批量删除MASObject; // 批量获取MASObject;
批量设置 Todo 已经完成:
var query = 'Todo';query;
不同类型的批量操作所引发不同数量的 API 调用,假设对象数量为n,fetchAll及saveAll发送n个请求,destroyAll发送1个请求。
4.2 查询
MAS.Query 是构建针对 MAS.Object 查询的基础类。每次查询默认最多返回 10 条符合条件的结果,要更改这一数值,需要使用到limit方法。
4.2.1 创建查询
var query = 'Todo';
4.2.2 根据objectId进行查询
var query = 'Todo'; query;
4.2.3 条件查询
根据不同条件来过滤结果,比如查询最迫切需要完成的日程列表 Todo,此时基于 priority 构建一个查询就可以得到符合条件的对象:
var query = 'Todo';// 查询 priority 是 0 的 Todoquery;query;
每次查询默认最多返回 10条符合条件的结果,要更改这一数值,需要使用limit方法。 将以上逻辑用 SQL 语句表达:
SELECT * FROM Todo WHERE priority = 0
当多个查询条件并存时,它们之间默认为 AND 关系,即查询只返回满足了全部条件的结果。建立 OR 关系则需要使用 MAS.Query.or方法。
请注意,在简单查询中,如果对一个对象的同一属性设置多个条件,那么先前的条件会被覆盖,查询只返回满足最后一个条件的结果。例如,我们要找出优先级为 0 和 1 的所有 Todo,错误写法是:
var query = 'Todo'; query; query; query;
正确作法是使用 OR 关系 来构建条件。
4.2.3.1 比较查询
- 等于:equalTo
- 不等于: notEqualTo
- 大于:greaterThan
- 大于等于:greaterThanOrEqualTo
- 小于:lessThan
- 小于等于:lessThanOrEqualTo
利用上述介绍的逻辑操作的接口,我们可以很快地构建条件查询。
例如,查询优先级小于 2 的所有 Todo :
var query = 'Todo';query;
要查询优先级大于等于 2 的 Todo:
query;
4.2.3.2 正则匹配查询
正则匹配查询是指在查询条件中使用正则表达式来匹配数据,查询指定的 key 对应的 value 符合正则表达式的所有对象。 例如,要查询标题包含中文的 Todo 对象可以使用如下代码:
var query = 'Todo';var regExp = '[\u4e00-\u9fa5]' 'i';query;query;
正则匹配查询只适用于字符串类型的数据。
4.2.3.3 包含查询
包含查询类似于传统 SQL 语句里面的 LIKE %keyword% 的查询,比如查询标题包含「龙神」的 Todo:
query;
翻译成 SQL 语句就是:
SELECT * FROM Todo WHERE title LIKE '%龙神%'
不包含查询与包含查询是对立的,不包含指定关键字的查询,可以使用 正则匹配方法 来实现。例如,查询标题不包含「机票」的 Todo,正则表达式为 ^((?!机票).)*$:
var query = 'Todo';var regExp = '^((?!机票).)*#39;, 'i');query.matches('title', regExp);
但是基于正则的模糊查询有两个缺点:
- 当数据量逐步增大后,查询效率将越来越低
- 没有文本相关性排序
还有一个接口可以精确匹配不等于,比如查询标题不等于「出差、休假」的 Todo 对象:
var query = 'Todo';var filterArray = '出差' '休假';query;
4.2.3.3 数组查询
当一个对象有一个属性是数组的时候,针对数组的元数据查询可以有多种方式。例如,在 数组 一节中我们为 Todo 设置了 reminders 属性,它就是一个日期数组,现在我们需要查询所有在 8:30 会响起闹钟的 Todo 对象:
var query = 'Todo';var reminderFilter = +'2015-11-11 08:30:00';query; // 也可以使用 equals 接口实现这一需求var targetDateTime = +'2015-11-11 08:30:00';query;
如果你要查询精确匹配 8:30、9:30 这两个时间点响起闹钟的 Todo,可以使用如下代码:
var query = 'Todo';var reminderFilter = +'2015-11-11 08:30:00' +'2015-11-11 09:30:00';query;
注意这里是精确关系,假如有一个 Todo 会在 8:30、9:30、10:30 响起闹钟,它不会被查询出来的。
如果要使用类似于SQL的IN操作,那么可以使用 containedIn 和 notContainedIn :
var query = 'Todo';var reminderFilter = +'2015-11-11 08:30:00' +'2015-11-11 09:30:00';query;
这里变为了包含关系,假如有一个 Todo 会在 8:30、9:30、10:30 响起闹钟,它会被查询出来的。
4.2.3.4 字符串匹配
使用 startsWith 可以过滤出以特定字符串开头的结果,这有点像 SQL 的 LIKE 条件。因为支持索引,所以该操作对于大数据集也很高效。
// 找出开头是「早餐」的 Todovar query = 'Todo';query;
另外你也可以使用endWith,但它与matches一样不支持索引:
// 找出结尾是「早餐」的 Todovar query = 'Todo';query;
4.2.3.5 OR查询
OR 操作表示多个查询条件符合其中任意一个即可。 例如,查询优先级是大于等于 3 或者已经完成了的 Todo:
var priorityQuery = 'Todo';priorityQuery; var statusQuery = 'Todo';statusQuery; var query = MASQuery;// 返回 priority 大于等于 3 或 status 等于 1 的 Todo
4.2.3.6 查询结果
例如很多应用场景下,只要获取满足条件的一个结果即可,例如获取满足条件的第一条 Todo:
var query = 'Comment';query;query;
为了防止查询出来的结果过大,云端默认针对查询结果有一个数量限制,即 limit,它的默认值是 10。比如一个查询会得到 10000 个对象,那么一次查询只会返回符合条件的 100 个结果。limit 允许取值范围是 1 ~ Number.MAV_VALUE。例如设置返回 10 条结果:
var query = 'Todo';var now = +;query;//查询今天之前创建的 Todoquery;// 最多返回 100 条结果
注意,我们不太建议设定太大的limit,这样会导致数据查询及传输很慢致使压垮数据库。
设置 skip 这个参数可以告知云端本次查询要跳过多少个结果。将 skip 与 limit 搭配使用可以实现翻页效果,这在客户端做列表展现时,尤其在数据量庞大的情况下就使用技术。例如,在翻页中,一页显示的数量是 10 个,要获取第 3 页的对象:
var query = 'Todo';var now = +;query;//查询今天之前创建的 Todoquery;// 最多返回 10 条结果query;// 跳过 20 条结果
通常列表展现的时候并不是需要展现某一个对象的所有属性,例如,Todo 这个对象列表展现的时候,我们一般展现的是 title 以及 content,我们在设置查询的时候,也可以告知云端需要返回的属性有哪些,这样既满足需求又节省了流量,也可以提高一部分的性能,代码如下:
var query = 'Todo';query;query;
4.2.3.7 统计总数
通常用户在执行完搜索后,结果页面总会显示出诸如「搜索到符合条件的结果有 1020 条」这样的信息。例如,查询一下今天一共完成了多少条 Todo:
var query = 'Todo';query;query;
4.2.3.8 排序
对于数字、字符串、日期类型的数据,可对其进行升序或降序排列。
// 按时间,升序排列query; // 按时间,降序排列query;
一个查询可以附加多个排序条件,如按 priority 升序、createdAt 降序排列:
var query = 'Todo';query;query;
4.2.3.9 查询性能优化
影响查询性能的因素很多。特别是当查询结果的数量超过 10 万,查询性能可能会显著下降或出现瓶颈。以下列举一些容易降低性能的查询方式,开发者可以据此进行有针对性的调整和优化,或尽量避免使用。
- 不等于和不包含查询(无法使用索引)
- 通配符在前面的字符串查询(无法使用索引)
- 有条件的 count(需要扫描所有数据)
- skip 跳过较多的行数(相当于需要先查出被跳过的那些行)
- 无索引的排序(另外除非复合索引同时覆盖了查询和排序,否则只有其中一个能使用索引)
- 无索引的查询(另外除非复合索引同时覆盖了所有条件,否则未覆盖到的条件无法使用索引,如果未覆盖的条件区分度较低将会扫描较多的数据)
4.3 用户
用户系统几乎是每款应用都要加入的功能。除了基本的注册、登录和密码重置,甚至还会使用手机号一键登录、短信验证码登录等功能。
MAS.User 是用来描述一个用户的特殊对象,它是 MAS.Object的子类 ,与之相关的数据都保存在 _User 数据表中,其默认fetchWhenSave为true。
4.3.1 用户的属性
用户名、密码、邮箱及电话是默认提供的四个属性,访问方式如下:
var user = ;user;user;user;
用户对象和普通对象一样也支持添加自定义属性。例如,为当前用户添加年龄属性:
var user = ;user;user;user;
4.3.2 注册
例如,注册一个用户的示例代码如下(用户名 Tom 密码 cat!@#123):
var user = ;user;user;user;user;user;
请注意,MAS并不会加密你的密码,因此你需要自己对密码进行加密处理。
4.3.3 登录
var user = ;user;user;user;
4.3.4 当前用户
开微博或者微信,它不会每次都要求用户都登录,这是因为它将用户数据缓存在了客户端。同样,只要是调用了登录相关的接口,MAS JS SDK 都会自动缓存登录用户的数据。 例如,判断当前用户是否为空,为空就跳转到登录页面让用户登录,如果不为空就跳转到首页:
var currentUser = MASUsercurrent;if currentUser // 跳转到首页else //currentUser 为空时,可打开用户注册界面…
4.3.4 SessionToken
所有登录接口调用成功之后,云端会返回一个 SessionToken 给客户端,客户端在发送 HTTP 请求的时候,JavaScript SDK 会在 HTTP 请求里面自动添加上当前用户的 SessionToken 和其objectId 作为这次请求发起者 MAS.User 的身份认证信息。
4.3.5 用户查询
查询用户代码如下:
var query = MASUser;
4.3 角色
角色可以被称为组,其目的是为了将对应的user进行分类,比如:CEO、CTO、运营、技术、产品等。
MAS.Role 是用来描述一个组的特殊对象,它同样是 MAS.Object的子类 ,与之相关的数据都保存在 _Role 数据表中,其默认fetchWhenSave为true。
4.3.1 角色的属性
名称和用户是默认提供的两个属性,访问方式如下:
var role = ;role;role;
4.3.2 添加用户
调用addUser方法,可以将一个用户添加到角色中:
var role = ;role;role;
4.3.2 删除用户
调用removeUser方法,可以将一个用户从角色中删除:
var role = ;role;role;
4.3.3 角色查询
查询用户代码如下:
var query = MASRole;
5. ACL
数据安全在应用开发的任何阶段都应该被重视。因此在这里我们对MAS的ACL记性讨论,如何使用MAS提供的安全功能模块为应用以及数据提供安全保障。
列举一个场景: 假设我们要做一个极简的论坛:用户只能修改或者删除自己发的帖子,其他用户则只能查看。
5.1 基于用户的权限管理
5.1.1 单用户权限设置
以上需求在 MAS 中实现的步骤如下:
- 写一篇帖子
- 设置帖子的「读」权限为所有人可读。
- 设置帖子的「写」权限为作者可写。
- 保存帖子
实例代码如下:
// 新建一个帖子对象 var Post = MASObject; var post = ; post; // 新建一个 ACL 实例 var acl = ; acl; acl; // 将 ACL 实例赋予 Post 对象 post; post;
以上代码产生的效果在 MAS平台的Post 表 可以看到,这条记录的 ACL 列上的值为:
"*":"read":true"55b9df0400b0f6d7efaa8801":"write":true
此时,这种 ACL 值的表示:所有用户均有「读」权限,而 objectId 为 55b9df0400b0f6d7efaa8801 拥有「写」权限,其他用户不具备「写」权限。
5.1.2 多用户权限设置
假如需求增加为:帖子的作者允许某个特定的用户可以修改帖子,除此之外的其他人不可修改。 实现步骤就是额外指定一个用户,为他设置帖子的「写」权限:
// 创建一个针对 User 的查询 var query = '_User'; query;
执行完毕上面的代码,回到MAS系统,可以看到,该条 Post 记录里面的 ACL 列的内容如下:
"*":"read":true"55b9df0400b0f6d7efaa8801":"write":true"55f1572460b2ce30e8b7afde":"write":true
从结果可以看出,该条 Post 已经允许 Id 为 55b9df0400b0f6d7efaa8801 以及 55f1572460b2ce30e8b7afde 两个用户(MAS.User)可以修改,他们拥有 write:ture 的权限,也就是「写」权限。
基于用户的权限管理比较简单直接,理解起来成本较低。
5.1.3 局限性探讨
再进一步的场景: 论坛升级,需要一个特定的管理员(Administrator)来统一管理论坛的帖子,他可以修改帖子的内容,删除不合适的帖子。
论坛升级之后,用户发布帖子的步骤需要针对上一小节做如下调整:
- 写一篇帖子
- 设置帖子的「读」权限为所有人。
- 设置帖子的「写」权限为作者以及管理员
- 保存帖子
我们可以设想一下,每当论坛产生一篇帖子,就得为管理员添加这篇帖子的「写」权限。
假如做权限管理功能的时候都依赖基于用户的权限管理,那么一旦产生变化就会发现这种实现方式的局限性。
比如新增了一个管理员,新的管理员需要针对目前论坛所有的帖子拥有管理员应有的权限,那么我们需要把数据库现有的所有帖子循环一遍,为新的管理员增加「写」权限。
假如论坛又一次升级了,付费会员享有特殊帖子的读权限,那么我们需要在发布新帖子的时候,设置「读」权限给部分人(付费会员)。这需要查询所有付费会员并一一设置。
毫无疑问,这种实现方式是完全失控的,基于用户的权限管理,在针对简单的私密分享类的应用是可行的,但是一旦产生需求变更,这种实现方式是不被推荐的。
5.1.4 基于角色的权限设置
管理员,会员,普通用户这三种概念在程序设计中,被定义为「角色」。 我们可以看出,在列出的需求场景中,「权限」的作用是用来区分某一数据是否允许某种角色的用户进行操作。
「权限」只和「角色」对应,而用户也和「角色」对应,为用户赋予「角色」,然后管理「角色」的权限,完成了权限与用户的解耦。
因此我们来解释 MAS 中「权限」和「角色」的概念。
「权限」在 MAS 服务端只存在两种权限:读、写。 「角色」在 MAS 服务端没有限制,唯一要求的就是在一个应用内,角色的名字唯一即可,至于某一个「角色」在当前应用内对某条数据是否拥有读写的「权限」应该是有开发者的业务逻辑决定,而 MAS 提供了一系列的接口帮助开发者快速实现基于角色的权限管理。
为了方便开发者实现基于角色的权限管理,MAS在 SDK 中集成了一套完整的 ACL (Access Control List) 系统。通俗的解释就是为每一个数据创建一个访问的白名单列表,只有在名单上的用户(MAS.User)或者具有某种角色(MAS.Role)的用户才能被允许访问。
为了更好地保证用户数据安全性, MAS 表中每一张都有一个 ACL 列。当然,MAS 还提供了进一步的读写权限控制。
一个 User 必须拥有读权限(或者属于一个拥有读权限的 Role)才可以获取一个对象的数据,同时,一个 User 需要写权限(或者属于一个拥有写权限的 Role)才可以更改或者删除一个对象。下面列举几种常见的 ACL 使用范例。
5.1.5 ACL 权限管理
5.1.5.1 默认权限
在没有显式指定的情况下,LeanCloud 中的每一个对象都会有一个默认的 ACL 值。这个值代表了所有的用户对这个对象都是可读可写的。此时你可以在数据管理的表中 ACL 属性中看到这样的值:
"*":"read":true"write":true
在 基于用户的权限管理 中,已经在代码里面演示了通过 ACL 来实现基于用户的权限管理,那么基于角色的权限管理也是依赖 ACL 来实现的,只是在介绍详细的操作之前需要介绍「角色」这个重要的概念。
5.1.6 角色的权限管理
5.1.6.1 角色的创建
首先,我们来创建一个 Administrator 的角色。
这里有一个需要特别注意的地方,因为 MAS.Role 本身也是一个 AVObject,它自身也有 ACL 控制,并且它的权限控制应该更严谨,如同「论坛的管理员有权力任命版主,而版主无权任命管理员」一样的道理,所以创建角色的时候需要显式地设定该角色的 ACL,而角色是一种较为稳定的对象:
// 新建一个角色,并把为当前用户赋予该角色 var roleAcl = ; roleAcl; roleAcl; // 当前用户是该角色的创建者,因此具备对该角色的写权限 roleAcl; //新建角色 var administratorRole = 'Administrator' roleAcl; administratorRole;
执行完毕之后,可以查看 _Role 表里已经存在了一个 Administrator 的角色。 另外需要注意的是:可以直接通过 系统的权限设置 直接设置权限。并且我们要强调的是:
ACL 可以精确到 Class,也可以精确到具体的每一个对象(表中的每一条记录)。
5.1.6.2 为对象设置角色的访问权限
我们现在已经创建了一个有效的角色,接下来为 Post 对象设置 Administrator 的访问「可读可写」的权限,设置成功以后,任何具备 Administrator 角色的用户都可以对 Post 对象进行「可读可写」的操作了:
// 新建一个帖子对象 var Post = MASObject; var post = ; post; // 新建一个角色,并把为当前用户赋予该角色 var administratorRole = 'Administrator'; //为当前用户赋予该角色 administratorRole; //角色保存成功 administratorRole;
5.1.6.3 用户角色的赋予和剥夺
经过以上两步,我们还差一个给具体的用户设置角色的操作,这样才可以完整地实现基于角色的权限管理。
在通常情况下,角色和用户之间本是多对多的关系,比如需要把某一个用户提升为某一个版块的版主,亦或者某一个用户被剥夺了版主的权力,以此类推,在应用的版本迭代中,用户的角色都会存在增加或者减少的可能,因此,MAS 也提供了为用户赋予或者剥夺角色的方式。 注意:在代码级别,为角色添加用户 与 为用户赋予角色 实现的代码是一样的。 此类操作的逻辑顺序是:
- 赋予角色:首先判断该用户是否已经被赋予该角色,如果已经存在则无需添加,如果不存在则将该用户(MAS.User)添加到角色实例中。
// 构建 MAS.Role 的查询 var roleQuery = '_Role'; roleQuery; roleQuery;
角色赋予成功之后,基于角色的权限管理的功能才算完成。
另外,此处不得不提及的就是角色的剥夺:
- 剥夺角色: 首先判断该用户是否已经被赋予该角色,如果未曾赋予则不做修改,如果已被赋予,则将对应的用户(MAS.User)从该角色中删除。
// 构建 MAS.Role 的查询var roleQuery = '_Role';roleQuery;roleQuery;
6. 缓存
MAS.Cache是对Redis的代理,Cache能加快查询,减少数据库的压力,目前Cache能支持大部分的Redis方法(但例如pub和sub是不被允许的),如果不熟悉Redis的API,你可以从这里学习如何使用。
例如我们使用缓存来规避用户反复提交数据,其实现如下:
var MAX_SUBMIT_COUNT = 20;var objectId = MASUsercurrent;var key = 'qmtv_cache_' + objectId;MASCache;
7. HTTP请求代理
在开发过程中,我们可能会对一些接口进行HTTP请求,例如请求PC主站的主播列表,或者说我们需要去调用后端的Service接口。一般情况下我们可以通过CORS来进行跨域,但是为了安全起见,PC主站或一些Service接口只允许特定域下的CORS,这个时候我们就可以使用HTTP请求代理功能方便的获得这些数据,例如:
MASHttpProxy; // application/www-form-urlencodeMASHttpProxy; // multipart/form-dataMASHttpProxy;
注意,在node环境中,文件只需要是一个文件路径即可,但在浏览器环境中,需要传入file对象。
8. 邮件
// 单发邮件MAS; // 群发邮件MAS; // 携带文件MAS;
注意,在node环境中,文件只需要是一个文件路径即可,但在浏览器环境中,需要传入file对象。
9. 短信
// 单发短信MAS; // 群发短信MAS;
10. 文件上传
MAS
11. 报表服务
在MIS开发中我们会涉及到导出功能,为了满足这一需求,我们抽象了报表服务,快速的进行报表的开发,其使用涉及到create/writeHeaders/writeData/close四个API
var report = ;report