接口编程 / 函数型接口"/>
筋斗云接口编程 / 函数型接口
如果不是典型的对象增删改查操作,可以设计函数型接口,比如登录、修改密码、上传文件这些。
函数型接口一般实现在文件 php/api_functions.php 中,它被主文件api.php包含。
假设有以下接口定义:
获取登录信息(who am i?)whoami() -> {id}应用逻辑
- 权限:AUTH_USER (必须用户登录后才可用)
我们使用模拟数据实现接口,函数名规范为api_{接口名}
:
function api_whoami()
{checkAuth(AUTH_USER);return ["id" => 100];
}
在api_functions.php中,作为示例,其中已经定义了登录、退出等接口,实际开发时在其基础上修改即可。
由于登录与权限定义密切相关,为了了解原理,我们清空这个文件,重新来写登录、退出接口。
同时学习获取参数、数据库操作等常用函数。
[任务]
本节要求实现登录、退出、取登录信息三个接口,设计如下:
登录接口
login(uname, pwd, _app?=user) -> {id, _isNew?}用户或员工登录(通过_app参数区分),如果是用户登录且用户不存在,可自动创建用户。参数
- _app: 前端应用名称,用于区分登录类型,"user"-用户端, "emp"-员工端。返回
- _isNew: 如果是新注册用户,该字段为1,否则不返回此字段。应用逻辑
- 权限: AUTH_GUEST
- 对于用户登录(_app是"user"),如果用户不存在,则自动创建用户。
- 密码采用md5加密保存
取登录信息
whoami() -> {id}如果已登录,则返回与登录接口相同的信息,否则返回未登录错误。
用户端或员工端均可用。
客户端可调用本接口测试是否可以通过重用会话,实现免登录进入。应用逻辑
- 权限:AUTH_USER | AUTH_EMP
退出接口
logout()退出登录。用户端或员工端均可用。应用逻辑
- 权限:AUTH_USER | AUTH_EMP
在接口定义中,一般包括接口原型,参数及返回数据说明,应用逻辑等。
对于含义清晰的参数和返回数据,也不必一一说明。
应用逻辑中应先规定该接口的权限。
权限定义
在实现接口前,我们先了解如何定义权限。
权限定义在接口应用的主文件api.php中,打开它我们能看到登录类型和权限类型的定义:
const AUTH_GUEST = 0;
// 登陆类型
const AUTH_USER = 0x01;
const AUTH_EMP = 0x02;
const AUTH_ADMIN = 0x04;// AUTH_LOGIN是一个特殊的权限,表示任一身份已登录。
define("AUTH_LOGIN", AUTH_USER | AUTH_EMP | AUTH_ADMIN);// 权限类型
const PERM_MGR = 0x08;
const PERM_TEST_MODE = 0x1000;
const PERM_MOCK_MODE = 0x2000;$PERMS = [AUTH_GUEST => "guest",AUTH_USER => "user",AUTH_EMP => "employee",AUTH_ADMIN => "admin",PERM_MGR => "manager",PERM_TEST_MODE => "testmode",PERM_MOCK_MODE => "mockmode",
];
上面按二进制位数不同,定义登录类型和各类权限,测试模式与模拟模式也可当作特殊的权限来对待。
在全局变量$PERMS中,为每个权限指定了一个可读的名字。
然后定义有一个重要的回调函数onGetPerms
,它将根据登录情况、session中的数据或全局变量来取出所有当前可能有的权限,
后面常用的检查权限的函数hasPerm/checkAuth都将调用它:
function onGetPerms()
{$perms = 0;if (isset($_SESSION["uid"])) {$perms |= AUTH_USER;}else if (isset($_SESSION["empId"])) {$perms |= AUTH_EMP;}...if (@$GLOBALS["TEST_MODE"]) {$perms |= PERM_TEST_MODE;}...return $perms;
}
在登录成功时,我们应设置相应的session变量,如用户登录成功设置$_SESSION["uid"]
,员工登录成功设置$_SESSION["empId"]
,等等。
后面讲对象型接口时,还会有另一个重要的回调函数onCreateAC
,用于将权限与类名进行绑定。
登录与退出
上节我们已经了解到,登录与权限检查密切相关,需要将用户信息存入session中,登录接口的大致实现如下:
function api_login()
{$type = getAppType();if ($type == "user") {... 验证成功 ...$_SESSION["uid"] = ...}else if ($type == "emp") {... 验证成功 ...$_SESSION["empId"] = ...}...
}
定义一个函数型接口,函数名称一定要符合 api_{接口名}
的规范。接口名以小写字母开头。
在接口实现时,一般应根据接口中的权限说明,使用checkAuth函数进行权限检查。
// 按设计要求,用md5加密后保存密码。
function hashPwd($pwd)
{return md5($pwd);
}function api_login()
{$type = getAppType();$uname = mparam("uname");$pwd = mparam("pwd");// 用户登录,如不存在则自动创建新用户if ($type == "user") {$sql = sprintf("SELECT id,pwd FROM User WHERE uname=%s", Q($uname));$row = queryOne($sql, PDO::FETCH_ASSOC);if ($row === false) {// 自动注册新用户$sql = sprintf("INSERT INTO User (uname, pwd) VALUES (%s, '%s')", Q($uname), hashPwd($pwd));$id = execOne($sql, true);$ret = ["id" => $id,"_isNew" => 1];}else if (hashPwd($pwd) != $row["pwd"]) {throw new MyException(E_AUTHFAIL, "bad password", "密码错误");}else {$ret = ["id" => $row["id"] ];}$_SESSION["uid"] = $ret["id"];}// 员工登录else if ($type == "emp") {$sql = sprintf("SELECT id,pwd FROM Employee WHERE uname=%s", Q($uname));$row = queryOne($sql, PDO::FETCH_ASSOC);if ($row === false || hashPwd($pwd) != $row["pwd"])throw new MyException(E_AUTHFAIL, "bad uname or password", "用户名或密码错误");$ret = ["id" => $row["id"] ];$_SESSION["empId"] = $row["id"];}else {throw new MyException(E_PARAM, "Unknown type `$type`");}return $ret;
}
在api_login函数中,先使用框架函数getAppType获取到登录类型(也称应用类型),再按登录类型分别查验身份,并最终设置$_SESSION
相关变量,
这里设置的变量与之前的权限回调函数onGetPerms
中相对应。
这里使用了很多常用函数,比如获取必需参数使用mparam函数,数据库查询使用了queryOne, execOne函数,出错返回使用MyException等,之后章节将详细介绍。
在实现whoami接口时,返回保存在会话(session)中的变量即可,logout接口则更加简单,直接销毁会话:
function api_whoami()
{checkAuth(AUTH_USER | AUTH_EMP);// 也可以用AUTH_LOGIN这个特殊的权限,表示任一身份已登录。// checkAuth(AUTH_LOGIN);if (hasPerm(AUTH_USER))return ["id"=> $_SESSION["uid"]];if (hasPerm(AUTH_EMP))return ["id"=> $_SESSION["empId"]];throw new MyException(E_SERVER);
}function api_logout()
{checkAuth(AUTH_LOGIN);session_destroy();
}
[应用标识与应用类型]
在筋斗云中,URL参数_app
称为前端应用标识(app),缺省为”user”,表示用户端应用。
不同应用要求使用不同的应用标识,在与后端的会话中使用的cookie也会有所不同,因而不同的应用即使同时在浏览器中打开也不会相互干扰。
应用标识中的主干部分称为应用类型(app type),例如有三个应用分别标识为”emp”(员工端), “emp2”(经理端)和”emp-store”(商户管理端),
它们的主干部分(去除尾部数字,去除”-“及后面部分)是相同的,都是”emp”,即它们具有相同的应用类型”emp”。
函数getAppType就是用来根据URL参数_app
取应用类型,不同的应用如果是相同的应用类型,则登录方式相同,比如上例中都是用员工登录。
更多推荐
筋斗云接口编程 / 函数型接口
发布评论