使用 CouchDB 和 Bootstrap 设计 Web 应用程序原型

将 Web 设计与数据库设计分开,然后再将它们融合在一起

将 Apache CouchDB 与 Twitter 的 Bootstrap 相结合来快速启动 Web 应用程序开发,这方式可以将表示层与数据库层的实现完全分开。CouchDB 作为一种能够以轻松的方式开发数据库而闻名,尤其对网站开发而言。在前端,出现了越来越多用于设计站点原型的开源工具包,甚至还有用于完整部署的工具包。其中,Bootstrap 框架对设计 Web UI 特别有用。本文将让您了解如何基于 Bootstrap 来构建应用程序并使用 CouchDB 实现持久性。

Uche Ogbuji, 合作伙伴, Zepheira, LLC

Uche Ogbuji 的照片Uche Ogbuji 是 Zepheira 的合作伙伴,主要负责监管复杂 Web 目录和其他丰富上下文数据库的创建。他在高级 Web 技术(比如 XML、语义 Web 和 Web 服务)、开源项目(比如 Akara,一种面向 Web 数据应用程序的开源平台)方面具有多年的开拓经验。他是一位计算机工程师兼作家,出生在尼日利亚,目前生活及工作在美国科罗拉多的博尔德附近。您可以通过他的网络日志 Copia 进一步了解 Ogbuji 先生。



2013 年 7 月 05 日

CouchDB 是一种新型的 NoSQL 数据库系统(DBMS)。主要信息存储为 JSON(JavaScript Object Notation,JavaScript 对象表示法)文档。CouchDB 还以托管附件的形式支持更通用的文档格式。它不支持经典的表格型数据,这些数据由 SQL 查询,已统治 DBMS 领域几十年。CouchDB 的另一个重要特征是,它的所有操作都以简单 HTTP 调用形式实现,尤其体现在它采用的 REST(Representational State Transfer,具象状态传输)风格接口。无论您采用何种主机平台或工具包,这种架构都简化了 CouchDB 的使用。它还使得 CouchDB 成为一个为网站和应用程序提供持久性后端的有用工具。

可以找到多篇介绍如何开始使用 CouchDB 的内置 JavaScript 库和相关工具(比如 CouchApp)开发应用程序的教程(参阅 参考资料)。在本文中,我将采用一种不同的方式,展示如何将 CouchDB 与简单的、静态提供的网站框架相结合,快速设计网站原型。

REST 简介

REST 是一种针对松散耦合的 Web 应用程序的架构风格,这些应用程序依靠指定的资源,例如统一资源定位符 (URL)、统一资源标识符 (URI) 和统一资源名称 (URN),而不是依靠消息。REST 利用了万维网的 HTTP 基础架构协议的某些方面,比如 GETPOST 请求。

一种快速构建网站前端的流行新方式是 Bootstrap。Bootstrap 系统由 Twitter 工程师开发,旨在帮助简化工程师们网站开发所用的平台和工具包。Bootstrap 为 Web 应用程序 UI 提供了一致的框架。它使设计漂亮的站点变得非常轻松,并使用级联样式表 (CSS) 来设计排版、表单、按钮、表格、网格、导航、警报等。在本文中,我们将学习如何快速启动开发流程,使用 Bootstrap 开发前端,使用 CouchDB 开发后端,甚至还会开发复杂的 Web 应用程序。

为什么不完全在 CouchDB 堆栈上执行原型设计?

尽管许多教程介绍了如何在 CouchDB 上轻松开发一个完整的网站,但这么做是不可取的。以这种方式着手开发会留下想要投机取巧并在站点上线时使用相同方法的巨大诱惑。从安全和维护角度讲,将后端数据库与前端表示层分开会更好一些。本文提供了在 CouchDB 实例上线时锁定它的各种选项,以保护所有敏感数据。安全性始终需要警觉的态度和专业的技能。因为基于静态文件的网站由来已久,所以它们已被广泛接受,而且它们的管理工具也最成熟。

将 CouchDB 与网站前端分开还有另一个优势,因为在 Web 开发期间通常存在多个框架:

  • DBMS 用于处理数据
  • Web 服务器或内容管理系统
  • Web 设计框架

在本文中,我使用了 CouchDB、Apache HTTP Server 和 Bootstrap。

人员、技能集和为这些层选择技术的标准常常各不相同。您希望一个方面的决策与另一个方面的决策松散耦合。除此之外,还有一些其他的部件,比如内容交付网络、测试框架、支持安全性的中间件等。越灵活地混搭这些组件,就能越容易在中长期内维护和改进系统。

我为本文选择的技术反映了我的数据库管理专长。Apache 和 Bootstrap 只是帮助我快速完成应用程序的工具。我期望在基本要素就绪后,能够与其他专家合作。中间件开发人员可能喜欢继续使用 Web 服务器框架,比如 Django 或 Ruby on Rails,甚至是内容管理系统,比如 WordPress,所以我使用的 Apache 是可被替换的,且不会影响其他层。Web 设计人员可能最希望进行手工设计而不是使用 Bootstrap,所以我使用的 Bootstrap 也可被轻松替换。得益于现代工具和框架,与依靠单个庞大的软件栈相比,采用这种方式设计 Web 应用程序原型并没有复杂多少。

入门

填充页面的容器

原型页面使用 jQuery 从 CouchDB 将文档加载到目标 div 中。我直接使用了 jQuery 的 Ajax 特性,而不是通过 CouchDB 和 CouchApp 提供的 jQuery 插件。该插件很方便,但它依赖于 CouchDB 与网站之间的紧密耦合,使用站点的页面作为数据库附件。我希望将这些方面分开,尤其是在开发期间。除此之外,该插件使您能够在需要时替换另一个后端。我用于加载页面的 JavaScript 非常简单;它包含在本文的代码 下载 中,文件名为 poquotes.js。

我会提供一个简单的 Web 应用程序演示:一个提供诗歌引语的站点。要获取安装 CouchDB 的帮助,请参阅 参考资料。在服务器上运行 CouchDB 之后,下载精简的 Twitter Bootstrap(它只是一个 CSS 和 JavaScript 代码包)或完整的项目包。本文使用的是完整的项目包。

我首先从 Twitter Bootstrap 包中的 fluid.html 示例开始。我对该文件进行了删减,修复了 CSS 和 JavaScript 脚本,以删除 ../assets/ 路径信息,还更新了实例站点的一些静态内容。结果(我保存为 index.html)位于本文的代码包中(参见 下载)。HTML 页标题的大部分代码如清单 1 所示:

清单 1. index.html 页标题的一部分
<!-- Styles -->
<link href="css/bootstrap.css" rel="stylesheet">
<style type="text/css">
  body {
    padding-top: 60px;
    padding-bottom: 40px;
  }
  .sidebar-nav {
    padding: 9px 0;
  }
</style>
<link href="css/bootstrap-responsive.css" rel="stylesheet">

<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
  <script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>

清单 2 是 index.html 表的一部分,特色引语会从 CouchDB 加载到该表中:

清单 2. 将特色引语加载到 index.html 表的某一部分
<div class="row-fluid">
  <div class="span4 featured-quote">
    <h2>Poet's name</h2>
    <p>Body of quote here.</p>
    <p><a class="btn" href="#">View details &raquo;</a></p>
  </div><!--/span-->
  <div class="span4 featured-quote">
    <h2>Poet's name</h2>
    <p>Body of quote here.</p>
    <p><a class="btn" href="#">View details &raquo;</a></p>
  </div><!--/span-->
  <div class="span4 featured-quote">
    <h2>Poet's name</h2>
    <p>Body of quote here.</p>
    <p><a class="btn" href="#">View details &raquo;</a></p>
  </div><!--/span-->
</div><!--/row-->

该页面还包含一种与数据库交互的机制。具体来讲,边栏包含一个表单,用户可使用它创建新的引语记录。用户单击一个链接来访问该表单,这会触发 jQuery 显示该表单。清单 3 给出了表单标记。(前两个 <input> 行被拆分,以适合本文的页宽限制。)

清单 3. 来自 index.html 的创建新引语记录的表单
<a href="#popup" id="popup">Click to create a new quote</a>
<div>
  <form id="newquote" action="index.html">
    <fieldset>
      <legend>The quote</legend>
      <label for="author">Author's Name</label>
      <input id="author" name="author" type="text" placeholder="First and last name" 
          required="required" autofocus="autofocus">
      <label for="text">Text of the quotation</label>
      <input id="text" name="text" type="textarea" placeholder="Text of the quote here" 
          required="required">
    </fieldset>
    <fieldset>
      <legend>The work</legend>
      <label for="title">Title of the work</label>
      <input id="title" name="title" type="text" required="required">
      <label for="link">Link to the work</label>
      <input id="link" name="link" type="text" required="required">
      <label for="year">Year of the work</label>
      <input id="year" name="year" type="text" required="required">
    </fieldset>
    <input id="donewquote" type="submit" value="Add quote" />
  </form>
</div>

清单 4 是接近 index.html 页面正文结尾的一段代码,用于加载必要的 JavaScript。最后的 script 行包含处理 CouchDB 中的 poquotes 数据库的具体代码。

清单 4. 在接近 index.html 页面正文末尾的地方加载 JavaScript
<!-- Placed at the end of the document so the pages load faster -->
  <script src="js/jquery-1.7.1.js"></script>
  <script src="js/bootstrap.js"></script>

  <script src="js/poquotes.js"></script>

</body>

在不到 5 分钟的时间内,Bootstrap 就让我完成了提供大部分现代特性的网站的基本设置,包括对旧版浏览器和移动设备的适当支持。对于您自己的网站,您可以更新颜色、字体、图像和其他细节以适合您的设计。您还可以访问 Bootstrap 图库来获得足够的灵感(参阅 参考资料)。


准备数据

清单 2 中,请注意 featured-quote 类的 div 元素。我将通过 CouchDB 将数据加载到这些元素中。主页包含六条著名的引语,它们由页面脚本动态加载并替换当前的样板语句。

转到 Futon(它是 CouchDB 的基于浏览器的控制台,比如 http://localhost:5984/_utils/),创建一个名为 poquotes 的数据库,如图 1 所示:

图 1. 从 CouchDB Futon 页面设置 poquotes 数据库
设置 poquotes 数据库的 CouchDB Futon 页面的屏幕截图

清单 5 是一个示例文档,它包含一条引语信息。(第 4 行被拆分为两行,以适合本文的页宽。)

清单 5. 一条引语的示例 JSON 文档
{
    "type": "quote",
    "author": "Thomas Hardy",
    "text": "And as the smart ship grew<br>In stature, grace, and hue<br>
In shadowy silent distance grew the Iceberg too.",
    "work": {
            "title": "The Convergence Of The Twain",
            "link": "http://www.poetryfoundation.org/poem/176678",
            "year": 1915
    }
}

我使用 cURL 将此文件以 q1.json 的形式加载到新数据库中:

curl -u user:passwd -X POST http://localhost:5984/poquotes \
 -H 'Content-Type: application/json' -d @q1.json
{"ok":true,"id":"ca42f5a16ca1905978afdeda68c116c2",
"rev":"1-7fe040eb6d322a35a86d2f871b100ff0"}

来自 CouchDB 的响应(以 {"ok" 开头)表明该文档已添加,并提供了为新文档生成的 ID 和修订号。CouchDB 的 RESTful 性质使设计站点数据集的原型就像操作文件系统那么简单。您也可以使用 Futon 添加文档,只要您喜欢。本文的可下载的 示例代码 包含 6 个这样的引语文档,这些已经足够填满目标页面的表格了。


准备主要查询

我使用 Futon 设计一个视图来加载该网页需要的文档。在 Futon 中,您可以设计临时视图,测试它们,然后保存它们供实际使用。如果可能的话,在开发周期中,数据库中仅有少量文档时,应尽早设计您的视图,因为临时视图可能很快就会变得很慢。清单 6 是视图的 JavaScript:

清单 6. 用于获取所有引语文档(按来源作品的年代建立索引)的 CouchDB 视图
function(doc) {
  if (doc.type == "quote") {
    emit(doc.work.year, doc);
  }
}

该视图很简单,但它完成了自己的任务,那就是按作品的年份对结果建立索引,因为这是我希望对页面中的记录排序的方式。要将此视图添加到您数据库中,可单击 View 选择器并切换到 Temporary view。然后,将 清单 6 粘贴到 Map Function 框中。单击 Run 可查看 6 个文档的表格清单。如果完成此操作,则应使用设计文档名 by_year 保存该视图。它不再是一个临时视图。然后可返回到可信赖的 cURL 来试用这个视图:

curl "http://localhost:5984/poquotes/_design/poquotes/_view/\
by_year?&descending=true&limit=6"

GET 参数 (&descending=true&limit=6) 调整数据库提供查询结果的方式。在本例中,只有 6 个最新的文档按升序返回,确保具有最新年份字段的文档最先返回。


跨域限制

多层 Web 应用程序的松耦合、快速原型设计是一个难点,浏览器中的一组跨域请求限制。出于安全原因,除了向与来源页面相同的主机上的相同端口发出请求之外,应严格限制浏览器脚本发出 HTTP 请求。目前已经有一些处理此问题的方法。一种方法是使用 JSONP(JSON with padding,请参阅 参考资料)。清单 7 摘自 poquotes.js,即将记录加载到目标 div 中的那一部分:

清单 7. 加载包含引语的目标页面的 JavaScript 和 jQuery 代码
...
    root: "http://localhost:5984/",
...
    max_quotes: 6,

    //Invoked when the HTML page is first loaded
    loadPage: function()
    {
        var six_latest = poq.root + "poquotes/_design/poquotes/_view/by_year?&limit="
            + poq.max_quotes + "&descending=true&callback=?";
        $.getJSON(six_latest, poq.handleMainQuotes);
...
    },

    //Invoked with the result of the Ajax call to load quote documents
    handleMainQuotes: function(json)
    {
        //Load up to six records, as available
        quote_count = Math.min(poq.max_quotes, json["total_rows"])
        for (var i=0; i<quote_count; i++) {
            var doc = json["rows"][i]["value"]
            var year = doc["work"]["year"].toString()
            var title = doc["work"]["title"].toString()
            var link = doc["work"]["link"].toString()

            //Create an HTML snippet from the fields of each quote document
            qblock = $("<div class='span4 featured-quote'></div>")
              .append("<h2>" + doc["author"] + "</h2>")
              .append("<p style='font-size: 80%; height: 8em;'>" + doc["text"] + "</p>")
              .append("<p>" + year + "</p>")
              .append("<p><a href='" + link + "'>" + title + "</a></p>")
              .append("<p><a class='btn' href='#'>View details &raquo;</a></p>")
            //jQuery's eq selector to find the target div corresponding to the loop index
            $('div.featured-quote:eq(' + i.toString() + ')').replaceWith(qblock);
        }
    },

清单 7 中,callback=? 添加在用于加载文档的 URL 的末尾。这告诉 jQuery 对 Ajax GET 查询使用 JSONP 从 CouchDB 获取文档,同时确保数据库在一个与 Web 前端不同的端口上运行。如果查询不同主机上的数据库,仍需要使用这种解决办法。

使用 GET 以外的方法发出 Ajax 请求会更加棘手。清单 8 摘自 poquotes.js,即创建一个新表单并将它发送到数据库的那一部分:

清单 8. 将字段从表单转换为记录并将它 POST 到 CouchDB 的 JavaScript 和 jQuery 代码
...
    dbroot: "db/",
...
    //Invoked when the HTML page is first loaded
    loadPage: function()
    {
...
        $('#donewquote').click(function() {
            var db_link = poq.dbroot + "poquotes";
            var record = {
                "type": "quote",
                "author": $("#author").val(),
                "text": $("#text").val(),
                "work": {
                    "title": $("#title").val(),
                    "link": $("#link").val(),
                    "year": parseInt($("#year").val())
                }
            };
            $.ajax({
                url : db_link,
                data : JSON.stringify(record),
                contentType : "application/json", 
                type : 'POST',
                processData : false,
                dataType : "json",
                success : function(resp) {
                    alert("New document created: " + JSON.stringify(resp));
                }
            });
            return false;
        });
        //Set up the collapsible form for adding new quotes
        $('#popup').click(function(){
            $("#newquote").slideToggle();
        });
        //Start out with the create quote form collapsed
        $("#newquote").slideToggle();
    },

清单 8 中,POST 是向相同的主机和端口发出的,但针对的是一个 /db/ URL。我使用了设置一个从该 URL 到 CouchDB 主机和端口的反向代理的技巧。更具体的说,我使用了一个 Apache HTTP Server ProxyPass 指令。此技术的细节依赖于您的设置,但它对设置一个在浏览器和服务器层之间严格分离的 Web 原型很有用。


外观

图 2 显示了在 div 填充了来自数据库的记录后,首次加载的有效的示例页面:

图 2. 从 Apache Web 服务器加载 index.html 后的浏览器屏幕
从 Apache 加载 index.html 后的浏览器屏幕的屏幕截图

用户可选择 Click to create a new quote 来显示添加一条新引语的表单,如图 3 所示:

图 3. 显示了表单的 index.html 的浏览器屏幕
显示了表单的 index.html 的浏览器屏幕的屏幕截图

结束语

在本文中,我解释了为什么喜欢使用某些技术设计 Web 应用程序的原型。它们使得 Web 设计和中间件工作更容易沿着独立、平行的路径进行,这感觉就像一种更加自然的关注点分离。我更喜欢拥有一个专门运行应用的 Web 服务器,而不是构建在 DMS 上,即使在开发环境中也是如此。

在本文中,我隐藏了 CouchDB 上的用户身份验证和授权的细节。在原型设计阶段之后,我更新了中间件来处理用户管理、会话、回复等功能。我可以将这些问题转交给 CouchDB 的工具来解决,或者使用独立的框架,具体情况取决于参与的项目。这是我获得的重要的灵活性。Web 应用程序获得了一些与登录和会话管理相关的细节,但它通常是一个独立层。

我在本文中的意图不是贬低在 NoSQL 平台上设计原型的其他方法,只是为了提供一种值得考虑的备用方法。将最有趣的开源项目相结合,专注于每个项目的核心优势,这样做既有趣又富有成效。在此过程中,您还可学习许多重要的细节,它们常常被用于使各层紧密地耦合工具所掩盖。


下载

描述名字大小
本文的样例 Web 应用程序couchdb-dw.zip9KB

参考资料

学习

获得产品和技术

讨论

  • 加入 developerWorks 社区。探索由开发人员推动的博客、论坛、群组和维基,并并与其他 developerWorks 用户进行交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development, Open source
ArticleID=935747
ArticleTitle=使用 CouchDB 和 Bootstrap 设计 Web 应用程序原型
publish-date=07052013