出自Yahoo_cn_wiki
您在这:首页 > NCP文档中心 > 入门篇 > 高级教程:创建公告板应用
高级教程:创建公告版应用
目录 |
如果按照Hello World描述的步骤,成功注册了自己的应用、并且通过浏览器访问得到正确的结果,那么恭喜你,好的开始是成功的一半。
这篇文档,我们继续开发之旅,设计并实现一个较为丰满的NCP 应用。这个应用将涉及到NCP相关信息的获取、判断以及用户输入信息获取、存储。
一些高级概念
在深入具体的例子之前,我们先来了解一下NCP中的几个高级概念:
- 用户角色
- 应用的多实例
应用的用户角色
NCP应用面向的用户,可以分为两个角色:
- 管理员:以站长天下为例,就是站点的站长或管理员,他们可以创建应用的实例(模块),并对模块进行配置;
- 使用者:以站长天下为例,就是站点的访客或普通成员,他们可以使用模块提供的功能,但无法修改模块的配置。
- 使用者有登录状态和非登录状态,在登录状态下,应用可以通过NCP系统参数获得用户的id
应用的多实例
一个好的应用,会被很多用户使用,例如在站长天下中,原创文章、视频分享等都是非常基础的应用,它们被成千上万的站长所使用。这些应用以不同的实例存在于不同的站点中,每一个实例称为一个模块。
象Hello, World这样的应用,显示的是静态的内容,每一个模块都是相同的。但是原创文章、视频分享等应用,不同的模块显示的是不同的数据,也就是说这些应用是支持多实例的。
下面我们要介绍的公告板应用,将会说明应用如何支持多实例。
关于多实例以及应用和模块的关系,详见应用和模块。
公告板的功能用例分析
接下来我们将一起来制作一个公告板应用。公告板的大致功能从应用的三大类用户的角度分析如下:
- 对于管理员:在站点上安装了公告板模块后,可以在其中输入并保存若干文字(公告),展示给站点的浏览者。
- 对于使用者:查看站长的输入的公告,如果是登录用户,多加一条固定的欢迎标语。
考虑到该应用将被安装到很多的站点,生成很多的模块,每个站长输入的公告文字都要保存起来。由于模块的id在NCP全局唯一,所以我们可以把公告保存到以模块id命名的文件中。
"公告板"应用的功能用例图如下:
进一步细化需求如下:
- 对于管理员:
- 公告板模块里的主要可见元素是编辑区和提交按钮;
- 如果是刚刚安装的模块(站长未发表任何公告)编辑区里面空白无内容;
- 如果已经输入过公告内容,再被打开,编辑区里将展示上次编写的内容供站长更改提交;
- 在提交公告信息时,应用必须进行必要的字数检查和标签过滤等功能。
- 对于使用者:
- 应用读出公告内容,展示成静态文本;
- 如果浏览者是一个登录用户,则显示一条欢迎标语。
应用处理逻辑中必要的参数及功能表述:
- 模块id号:用于定位存储公告信息的文件。
- 用户身份:用于判断是站长访问(展示编辑视图)还是浏览者访问(展示浏览视图)。
- 登录标识:用于判断是否显示欢迎标语。
目标已经提出,功能点已经细化,我们接着进入实施阶段。
公告板的制作过程
从伪代码到框架代码
简单起见,我们将所有的逻辑都放在一个PHP文件中实现,根据需求描述,我们可以写出如下的伪代码
获取模块id; 读取公告文件; 获取用户角色; if 当前用户不是站长 { 输出公告浏览界面; if 登录用户 { 输出欢迎标语; } } else { if 站长提交了新公告 { 新的公告存内容存入公告文件; } 输出公告编辑界面; }
进而我们可以写出初步的代码如下:
<?php header("Cache-Control: no-cache"); //======================================= //--- 1 --- 获取模块id,$module_id,用于定位公告文件,读取公告内容 //======================================= $notice_text = read_notice_file($module_id); //======================================= //--- 2 --- 判断用户角色 $is_admin; //======================================= //存放输出到页面的html $response_html = ''; if(!$is_admin) { //======================================= //--- 2.1 --- 生成浏览视图的逻辑 //======================================= } else { //======================================= //--- 2.2--- 生成站长视图的逻辑 //======================================= } //======================================= //--- 3 --- 返回应答结果 //======================================= echo $response_html; return; //======================================= //--- 4 --- 把信息从公告文件读出的函数 //======================================= function read_notice_file($module_id) { //读取文件逻辑 } //======================================= //--- 5 --- 将信息写入公告文件的函数 //======================================= function write_notice_file($module_id, $content) { //写入文件逻辑, 如果文件不存在,创建之 } ?>
获取NCP传入参数
在框架代码注释标号为1和2的地方都需要应用处理从NCP获取的参数。
NCP和应用之间最基本的交互方式是HTTP请求,一般流程是:
- 用户浏览器请求NCP;
- NCP请求应用的回调地址(同时传递部分参数);
- 应用根据NCP传递的参数给出应答;
- NCP根据应用的应答内容,组装好整个页面,返回给用户浏览器。
NCP和应用之间接口的详细文档,请参阅应用回调接口。
应用的回调接口地址是开发者在注册该应用时提供的。
假设:注册该应用时,几个重要的参数分别是
回调地址是:http://example.com/myapp/callback.php 应用路径是:notice 嵌入方式: 代码嵌入YNML 配置视图:无配置视图
NCP平台采用get方式请求回调地址,实际发送给应用的HTTP请求URL是:
http://example.com/myapp/callback.php?y_api_key=mQ6jFvxxxxxx840Zr1flamQ&y_iframe=0.......
参数规定的是NCP和应用之间的通信协议。详细介绍请参阅应用回调接口。
对于公告板应用,我们需要获取的变量有三个,它们都被直接放在调用应用的URL中,以便应用获取。
- y_module_id — 模块id。
- y_user_id — 用户id,仅当登录用户访问NCP站点时存在。
- y_user_role — 用户角色,仅当登录用户访问NCP站点时存在,例如如果y_user_role=1则说明是管理员,请他具体情况请参考应用回调接口。
如此注释标号为1的详细代码如下:
<?php //--- 1 --- 获取模块id, $module_id,用于定位公告文件,读取公告内容 $module_id = $_GET["y_module_id"]; ?>
注释标号为2的详细代码如下:
<?php //--- 2 --- 判断用户角色 $is_admin; $is_admin = ($_GET["y_user_role"] == "1") ? true : false; ?>
生成浏览视图的逻辑
下面我们为框架代码中注释标号为2.1的地方编写具体的处理逻辑。
按照需求,生成浏览视图的逻辑要区别对待登录用户和未登录用户,我们通过获取用户id来检验。
- 如果能够从HTTP请求中获取到用户id,则这是一个登录的用户发起的访问,输出界面将多出一条欢迎标语;
- 否则这是一个未登录的用户发起的访问,输出界面只有公告语。
<?php //--- 2.1 --- 生成浏览视图的逻辑 if (isset($_GET["y_user_id"])) { $response_html .= "<p>欢迎访问</p>"; } $response_html .= "<div>站长公告:<p>" . htmlspecialchars($notice_text) . "</p></div>"; ?>
生成站长编辑视图的逻辑
下面我们为框架代码中注释标号为2.2的地方编写具体的处理逻辑。
按照需求,这段逻辑为站长生成编辑公告语的视图,而且如果站长提交了新的公告语,这段逻辑还将做数据保存操作。
为了区分站长是在浏览已有的公告语,还是提交了新公告语,我们需要新增加一个标志update,将它放置在表单的隐藏域中,随着表单一并提交到完整视图的页面,本例表单提交的action地址是完整视图的入口地址(完整视图的入口地址计算方法是:http://{添加该应用的站点域名}/apps/{注册时填写的应用路径}/?module_id=xxx,详情请参看回调地址映射),NCP会原封不动地转发应用的表单,具体请参考NCP回调接口。
<?php //--- 2.2--- 生成站长视图的逻辑 if($_POST["update"] == "true") { $new_notice = $_POST["notice"]; if (write_notice_file($module_id, $new_notice)) { $notice_text = $new_notice; $response_html .= "<p>公告已更新</p>"; } else { $response_html .= "<p>公告更新失败</p>"; } } $response_html .= "<form method='post' style='text-align:center' action='/apps/notice/? module_id=".$module_id."'>" . " <input type='hidden' name='update' value='true'>" . " 当前公告语:<br/>" . " <textarea name='notice' rows='3' cols='20'>" . htmlspecialchars($notice_text) . " </textarea>" . " <input type='submit' value='更新公告'>" . "</form>"; ?>
文件操作逻辑
下面简单列出实现公告信息文件存取的代码:
//======================================= //--- 4 --- 把信息从公告文件读出的函数 //======================================= function read_notice_file($module_id) { if(file_exists("/tmp/".$module_id.".txt")) return file_get_contents( "/tmp/".$module_id.".txt" ); else return "暂未设置公告"; } //======================================= //--- 5 --- 将信息写入公告文件的函数 //======================================= function write_notice_file($module_id, $content) { return (file_put_contents( "/tmp/$module_id.txt", $content ) == strlen($content)); }
效果图和完整代码
<?php header("Cache-Control: no-cache"); //--- 1 --- 获取模块id, $module_id,用于定位公告文件,读取公告内容 $module_id = $_GET["y_module_id"]; //--- 2 --- 判断用户角色 $is_admin; $is_admin = ($_GET["y_user_role"] == "1") ? true : false; $notice_text = read_notice_file($module_id); //存放输出到页面的html $response_html = ''; if (!$is_admin) { if(FALSE == $notice_text) { echo "读取公告信息失败"; return; } //--- 2.1 --- 生成浏览视图的逻辑 if (isset($_GET["y_user_id"])) { $response_html .= "<p>欢迎访问</p>"; } $response_html .= "<div>站长公告:<p>" . htmlspecialchars($notice_text) . "</p></div>"; } else { //--- 2.2--- 生成站长视图的逻辑 if($_POST["update"] == "true") { $new_notice = $_POST["notice"]; if (write_notice_file($module_id, $new_notice)) { $notice_text = $new_notice; $response_html .= "<p>公告已更新</p>"; } else { $response_html .= "<p>公告更新失败</p>"; } } if(FALSE == $notice_text) $notice_text = "读取公告信息失败"; $response_html .= "<form method='post' style='text-align:center' action='/apps/notice/? module_id=".$module_id."'>" . " <input type='hidden' name='update' value='true'>" . " 当前公告语:<br/>" . " <textarea name='notice' rows='3' cols='20'>" . htmlspecialchars($notice_text) . " </textarea>" . " <input type='submit' value='更新公告'>" . "</form>"; } //--- 3 --- 返回应答结果 echo $response_html; return; //======================================= //--- 4 --- 把信息从公告文件读出的函数 //======================================= function read_notice_file($module_id) { if(file_exists("/tmp/".$module_id.".txt")) return file_get_contents( "/tmp/".$module_id.".txt" ); else return "暂未设置公告"; } //======================================= //--- 5 --- 将信息写入公告文件的函数 //======================================= function write_notice_file($module_id, $content) { return (file_put_contents( "/tmp/".$module_id.".txt", $content ) == strlen($content)); } ?>
应用调试
在前面的Hello, World!例子中,已经详细介绍了应用调试的步骤,这里就不再赘述。我们这里主要说明cache对调试的影响。
在调试应用时,有可能出现修改代码之后,页面内容没有出现预期的变化。这是由于默认情况下NCP对应用的输出会自动进行缓存,缓存时间为240分钟(即4小时)。请求得到的数据则直接从缓存里取出的,应用上新的逻辑就不会执行。具体情况请参考应答的缓存。
如果为了输出动态内容而需要禁止缓存,可以使用以下方法:
- 方法一:这是推荐的做法。在应用的HTTP应答中加入禁止缓存的报头:
Cache-Control: no-cache
例如PHP代码中可以这样写:header("Cache-Control: no-cache") - 方法二:在回调地址的参数中加入一个任意变动的哑参数(例如当前时间或随机数),就可以让缓存不能命中。我们并不推荐这样做,因为实际应答还是会被NCP缓存,如果哑参数的值处理不当,可能会导致意想不到的结果,例如A用户看到了B用户的数据。
-
提示
- NCP的应用缓存机制是为了提高响应速度和降低对应用服务器的压力。应用可以充分利用这个特性减少NCP对应用的请求数量。如果用第一种方法调试时,在发布时,别忘记把Cache-Control报头设置成合理的值,详情请参看应答的缓存。

