出自Yahoo_cn_wiki

跳转到: 导航, 搜索

您在这:首页 > NCP文档中心 > 入门篇 > 高级教程:创建公告板应用

高级教程:创建公告版应用

目录

如果按照Hello World描述的步骤,成功注册了自己的应用、并且通过浏览器访问得到正确的结果,那么恭喜你,好的开始是成功的一半。

这篇文档,我们继续开发之旅,设计并实现一个较为丰满的NCP 应用。这个应用将涉及到NCP相关信息的获取、判断以及用户输入信息获取、存储。

一些高级概念

在深入具体的例子之前,我们先来了解一下NCP中的几个高级概念:

  1. 用户角色
  2. 应用的多实例

应用的用户角色

NCP应用面向的用户,可以分为两个角色:

  1. 管理员:以站长天下为例,就是站点的站长或管理员,他们可以创建应用的实例(模块),并对模块进行配置;
  2. 使用者:以站长天下为例,就是站点的访客或普通成员,他们可以使用模块提供的功能,但无法修改模块的配置。
  • 使用者有登录状态和非登录状态,在登录状态下,应用可以通过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请求,一般流程是:

  1. 用户浏览器请求NCP;
  2. NCP请求应用的回调地址(同时传递部分参数);
  3. 应用根据NCP传递的参数给出应答;
  4. 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用户的数据。
Image:note.png 提示
NCP的应用缓存机制是为了提高响应速度和降低对应用服务器的压力。应用可以充分利用这个特性减少NCP对应用的请求数量。如果用第一种方法调试时,在发布时,别忘记把Cache-Control报头设置成合理的值,详情请参看应答的缓存
个人工具