为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

2014-11-03  来源:本站原创  分类:编程  人气:1 

在当今这个协作和社交应用的世界里,其关键是要有一个能简单构建和易于部署的后台。许多组织机构都依赖于一个应用栈(Application Stack),其使用下面三项技术:

这个栈对于移动应用来说相当流行,因为原生数据格式是JSON,它容易被应用解析,例如通过使用 Cocoa 的NSJSONSerialization类或其它类似的解析器。

在本教程中,你将学会如何搭建了一个 Node.js 环境,驱动 Express;在此平台之上,你将构建一个通过 REST API 来提供一个 MongoDB 数据库的服务器,就像这样:

为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

在一个 HTML 表格中呈现的后端数据库

本教程的第二部分重点放在 iOS 应用端。你将构建一个很酷的叫做“有趣的地方”的应用,标记有趣的位置,让其它用户能够找出他们附近有趣的地方。下面稍微窥探一下你将构建的应用:

为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

TourMyTown 的主视图

本教程假设你已经了解了 JavaScript 和 Web 开发的基础,但对 Node.js、Express 以及 MongoDB 都不熟悉。

一个 Node+Mongo 案例

大多数 Objective-C 开发者都不太熟悉 JavaScript ,但它对于 Web 开发者来说是极其常见的语言。因为这个原因,Node 作为一个 Web 框架收获了大量人气,但还有更多原因使其成为后端服务的绝好选择:

  • 内建的服务器功能
  • 通过它的包管理器做到良好的项目管理
  • 一个快速的 JavaScript 引擎,也就是 V8
  • 异步事件驱动编程模型

一个异步的关于事件和回调的编程模型非常适合服务器,它要等待许多事情,例如到来的请求以及通过其它服务(例如 MongoDB)的内部进程通信。

MongoDB 是一个低开销的数据库,其所有实体都是自由形式 BSON —— “二进制 JSON” —— 文档。这能让你同异构数据打交道,而且处理各种各样的数据格式也变得很容易。因为 BSON 与 JSON 兼容,构建一个 REST API 就很简单——服务器代码能够传递请求到数据驱动器而不需要很多的中间处理。

Node 和 MongoDB 在本质上都具有可扩展性,能够轻松地在跨越分布式模型中的多个机器,实现同步;这个组合对于不具有均匀分布负载的应用来说是一个理想选择。

入门

本教程假设你使用 OS X Mountain Lion 或 Mavericks ,Xcode 及其 command line tools 都已经安装好了。

第一步是安装Homebrew。就像 CocoaPods 为 Cocoa 管理各种包 和 Gem 为 Ruby 管理各种包一样,Homebrew 管理 OS X 上的 Unix 工具。它构建在 Ruby 和 Git 之上,而且它具有高度的灵活性和可定制性。

如果你已经安装了 Homebrew ,那就可以跳过下面的步骤。不然,打开终端执行下列命令来安装 Homebrew :

ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)" 

注意:cURL是使用 URL 请求来发送和接收文件与数据的称手工具。此处你使用它加载 Homebrew 安装脚本——在本教程后面,你还会使用它与 Node 服务器交互。

一旦安装好 Homebrew ,就在终端输入下面的命令:

brew update 

这只是更新 Homebrew ,让你拥有最新的软件包列表。

现在,通过 Homebrew 安装 MongoDB ,使用下面的命令:

brew install mongodb 

记下 MongoDB 被安装的位置,它就在输出的“Summary”中。稍后你将用它加载 MongoDB 服务。

http://nodejs.org/download/下载并运行 Node.js 安装器。

一旦安装完成,你就马上测试 Node.js 是否安装成功。

在终端里输入:

node 

这能让你进入 Node.js 的交互式运行环境,在此你可以执行 JavaScript 表达式。

在提示符后输入下面的表达式:

console.log("Hello World"); 

你将得到如下输出:

Hello World
undefined

console.log在 Node.js 中相当于NSLog。当然,console的输出流比NSLog的要复杂得多:它有console.infoconsole.assertconsole.error以及你期望的从更先进的记录器例如CocoaLumberjack而来的其它流。

写在输出里的 “undefined” 值是console.log的返回值,而console.log没有返回值。 因为 Node.js 总是显示出所有表达式的输出,无论其返回值是否有定义。

注意:如果你以前使用过 JavaScript ,你需要知道 Node.js 环境和浏览器环境之间有些许不同。全局对象被叫做global而不是window。在 Node.js 交互提示符后键入global并按下回车就会显示 global 命名空间里所有的方法和对象;当然,直接使用Node.js 文档来做参考更容易些。 :]
global对象有所有预定义的常数、函数以及数据类型,都可用于所有运行在 Node.js 环境里的程序。任何用户创造的变量同样也都添加到全局上下文对象。基本上global的输出将列出所有内存中可以访问的事物。

运行一个 Node.js 脚本

Node.js 的交互式环境对于玩耍和调试 JavaScript 表达式是很棒的,但通常你都会使用脚本文件来做实际的事情。就像 iOS 应用包含有Main.m作为其入口点,Node.js 的默认入口点就是index.js。然而,不同于 Objective-C ,这里没有 main 函数;相反,index.js将从头到尾的执行。

按下 Control+C 两次以退出 Node.js Shell。执行下面的命令,新建一个目录以保存你的脚本:

mkdir ~/Documents/NodeTutorial 

然后执行下面的命令进入新建的目录并使用你默认的文本编辑器新建一个脚本文件:

cd ~/Documents/NodeTutorial/; edit index.js 

index.js中添加如下代码:

console.log("Hello World.");

保存你的工作,回到终端执行下面的命令看看你的脚本如何运行:

node index.js 

再一次,我们看到了熟悉的 “Hello World” 输出。你也可以执行node .来运行你的脚本,.就会默认查找index.js

为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

固然,一个 “Hello World” 脚本成不了一个服务器,但这是测试你的安装是否成功的快速方式。下一节将向你介绍 Node.js 包的世界,这会成为你那闪亮的新 Web 服务器的基础!

Node 包

Node.js 应用程序都被分成不同的包,这就是 Node.js 世界的“框架”。 Node.js 自带有几个基础且强大的包,但还有超过 50000 个由活跃的开发社区提供的公开包——如果你不能找到你需要的包,你自己也可以比较容易地创造。

注意:查看https://npmjs.org/可得到所有可用包的列表

用下列代码替换index.js的内容:

//1
var http = require('http');

//2
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('<html><body><h1>Hello World</h1></body></html>');
}).listen(3000);

console.log('Server running on port 3000.');

依次按照编号好的注释看看:

  1. require引入(import)模块(module)到当前文件。本次你引入了 HTTP 库。
  2. 你创建一个 Web 服务,它对简单的 HTTP 请求的回应是发送一个 200 应答,并将页面内容放在应答里。

Node.js 作为一个运行时环境的最大的优势之一就是他的事件驱动模型(event-driven model)。它围绕着异步调用的回调函数的概念来设计。在上面的例子里,你正监听 3000 端口等着传入的 HTTP 请求。当你收到一个请求,你的脚本调用function (req, res) {…}并返回一个应答给调用者。

保存你的文件,回到终端并执行如下命令:

node index.js 

你将在控制台看到如下输出:

为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

打开你最喜欢的浏览器导航至http://localhost:3000;好好瞧着, Node.js 正在提供给你的是一个 “Hello World” 页面。

为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

你的脚本还在哪里,耐心地等待从 3000 端口传入的 HTTP 请求。要干掉(kill)你的 Node 实例,只需在终端按下Ctrl+C

注意:Node 包通常由顶层函数或引入的对象写就。通过使用 require ,这个函数在之后就被分配给一个顶层变量。这样有助于以一个健全的方式管理范围(scope)以及暴露(expose)模块的 API 。稍后在本教程中你会看到如何创建一个自定义模块,你将为 MongoDB 添加一个驱动器。

NPM —— 使用外部 Node 模块

前一小节覆盖了 Node.js 内建的模块,那第三方的模块该怎么处理呢?例如你之后需要的 Express 模块,它为你的服务器平台提供路由中间件。

外部模块同样可以使用 require 函数引入到文件里,但你需要分开下载它们然后才能用于你的 Node 实例。

Node.js 使用 npm—— Node 包模块——来下载、安装以及管理包依赖。如果你熟悉 CocoaPods 或者 Ruby gems ,那么你对npm也会觉得熟悉。你的 Node.js 应用程序使用package.json,它专门定义配置和npm依赖。

使用 Express

Express 是一个流行的 Node.js 模块,提供路由中间件。为什么你会需要这个独立的包呢?考虑下面的情形。

如果你只使用http模块自身,你不得不分开解析每个请求的位置以找出提供什么内容给请求者——如此这般,事情很快就会变得难以处理。

然而,用 Express 你就能容易地为每个请求定义路由和回调。 Express 同样让为基于 HTTP 动词(例如 POST, PUT, GET, DELETE, HEAD, 等)以提供不同的回调变得很容易。

HTTP 动词的简要介绍

一个 HTTP 请求包含一个方式——或者动词——的值。默认值是GET,它是为了获取数据,例如浏览器中 Web 页面。POST意味着上传数据,例如提交 Web 表单。对于 Web API 来说,POST通常用于添加数据,但它同样可用于远程处理调用类型端点(but it can also be used for remote procedure call-type endpoints.)。

PUTPOST的不同在于它通常用于替换已有数据。在实践中,POSTPUT通常以同样的方式使用:在请求 Body 里提供实体以放进后端的数据存储里。DELETE用于从你的后端数据存储里移除条目。

POSTGETPUT以及DELETE就是 HTTP 实现的 CRUD 模型 —— Create、Read、Update 以及 Delete。

还有其它一些少有人知的 HTTP 动词。HEAD表现得像一个GET但只返回应答头而没有 Body 。这有助于最小化数据传输,如果应答头中的信息足够确定是否有可用的新数据。其它动词如TRACECONNECT用于网络路由。

添加一个包到 Node 实例

在终端里执行下列命令:

edit package.json 

这会创建一个新的package.json,它将包含你的包配置和依赖。

添加如下代码到package.json中:

{
  "name": "mongo-server",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "express": "3.3.4"
  }
}

这个文件定义了一些元数据,例如项目的名字和版本,一些脚本,以及对于你的目的来说最重要的包依赖。下面说明每行的意思:

  • name是项目的名字。
  • version是项目目前的版本。
  • private防止项目被意外地公开,如果你设置其为 true 。
  • dependencies是一个包含你应用使用的模块的列表。

依赖以 键/值 形式接受模块名和版本。你的依赖列表包含有 3.3.4 版本的 Express; 如果你想指明 Node.js 去使用最新版本的包,你可以使用通配符"*"。

保存文件,在终端里执行下列命令:

npm install 

你会看到如下输出:

为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

install下载并安装package.json指定的依赖——以及你的依赖本身的依赖!:] ——存进一个叫做node_modules的目录,并让你的应用程序使用它们。

一旦npm完成,你就可以在你的应用程序中使用 Express 了。

index.js中找到下列行:

var http = require('http');

并添加 Express 的require调用,如下所示:

var http = require('http'),
    express = require('express');

这就引入了 Express 包,并将其存在变量express中。

添加如下代码到index.js,就在刚在添加的区域的下面:

var app = express();
app.set('port', process.env.PORT || 3000);

这就创建了一个 Express 应用并设置其默认端口为 3000 。你可以通过创建一个环境变量PORT来覆盖此默认值。这种类型的自定义在开发工具中非常方便,特别是如果你有多个应用程序监听这好几个端口。

添加如下代码到index.js,就在刚刚添加的区域的下面:

app.get('/', function (req, res) {
  res.send('<html><body><h1>Hello World</h1></body></html>');
});

这就创建了一个路由处理器(route handler),它是给定 URL 的请求处理者链的花哨名字。Express 匹配请求中指定的路径并执行适当的回调。

你上面的回调告诉 Express 去匹配根路径 "/" 并返回一个给定的 HTML 。send为你格式化各种响应头——例如content-type

相关文章
  • 为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务 2014-11-03

    在当今这个协作和社交应用的世界里,其关键是要有一个能简单构建和易于部署的后台.许多组织机构都依赖于一个应用栈(Application Stack),其使用下面三项技术: Node.js Express MongoDB 这个栈对于移动应用来说相当流行,因为原生数据格式是JSON,它容易被应用解析,例如通过使用 Cocoa 的NSJSONSerialization类或其它类似的解析器. 在本教程中,你将学会如何搭建了一个 Node.js 环境,驱动 Express:在此平台之上,你将构建一个通过 R

  • 一个简单的Node.js异步操作管理器分享 2014-07-18

    这篇文章主要介绍了一个简单的Node.js异步操作管理器分享,需要的朋友可以参考下 最近写nodejs比较多,刚开始的时候碰到的异步的操作比较少,因为想做的东西比较简单,一查api有同步的,为了省事就直接用同步的搞了,慢慢发现这不是个事呀,好好的异步特性不用,非得用同步的,真?澹?⑶液芏喽?髂居型?降?pi的. 好!写异步的,慢慢的出现了这种代码... mysql.query('xxxx').on('success', function(){ mysql.query('xxxx').on('su

  • 一个用php3编写的简单计数器 2014-03-28

    php具有极其强大的图像处理能力,用它可以很轻易的动态生成web图像. 一下是一个使用php做成的一个简单计数器. 1. 总体思路: 把以往的访问人数记录在一个文本文件中,当网页被访问的时候,从打开该文件 并从中读出以往的访问人数,加 1,得到最新的访问人数,并把该数目格式化成 标准的格式,再调用图像处理函数,把该数字输出成图片,再把新的访问数字回 写到纪录访问人数的文件中. 2. 程序所用到的函数说明: A. 相关的文件操作: a. 打开文件: 函数的原型:int fopen(string f

  • 使用Node.js实现一个简单的FastCGI服务器实例 2013-10-30

    这篇文章主要介绍了使用Node.js实现一个简单的FastCGI服务器实例,也可以作为一个比较详细的Node.js服务器创建教程,需要的朋友可以参考下 本文是我最近对Node.js学习过程中产生的一个想法,提出来和大家一起探讨. Node.js的HTTP服务器 使用Node.js可以非常容易的实现一个http服务,最简的例子如官方网站的示例: var http = require('http'); http.createServer(function (req, res) { res.write

  • 简单案例:使用Node.JS在线渲染LESS CSS 2015-04-15

    什么是LESS CSS 一种动态样式语言.LESS将CSS赋予了动态语言的特性,如变量,继承,运算,函数.LESS既可以在客户端上运行,也可以借助Node.js在服务端运行. LESS CSS的使用 客户端的解析:可以在head将 rel 属性值为 "stylesheet/less",并引入<script src="less.js" type="text/javascript"></script> 命令行中使用:lessc

  • 使用Python编写一个简单的tic-tac-toe游戏的教程 2014-01-15

    这篇文章主要介绍了使用Python编写一个简单的tic-tac-toe游戏的教程,有利于Python初学者进行上手实践,需要的朋友可以参考下 这个教程,我们将展示如何用python创建一个井字游戏. 其中我们将使用函数.数组.if条件语句.while循环语句和错误捕获等. 首先我们需要创建两个函数,第一个函数用来显示游戏板: def print_board(): for i in range(0,3): for j in range(0,3): print map[2-i][j], if j !

  • 用Python编写一个简单的俄罗斯方块游戏的教程 2014-02-12

    这篇文章主要介绍了用Python编写一个简单的俄罗斯方块游戏的教程,编写俄罗斯方块几乎是每门编程语言基础学习后的必备实践,需要的朋友可以参考下 俄罗斯方块游戏,使用Python实现,总共有350+行代码,实现了俄罗斯方块游戏的基本功能,同时会记录所花费时间,消去的总行数,所得的总分,还包括一个排行榜,可以查看最高记录. 排行榜中包含一系列的统计功能,如单位时间消去的行数,单位时间得分等. 附源码: from Tkinter import * from tkMessageBox import *

  • 用Python编写一个简单的FUSE文件系统的教程 2014-05-26

    这篇文章主要介绍了用Python编写一个简单的FUSE文件系统的教程,对于数据的备份很有帮助,需要的朋友可以参考下 如果你是我的长期读者,那么你应该知道我在寻找一个完美备份程序,最后我写了一个基于bup的我自己的加密层. 在写encbup的时候,我对仅仅恢复一个文件就必须要下载整个巨大的档案文件的做法不甚满意,但仍然希望能将EncFS和 rdiff-backup一起使用来实现可远程挂载.加密.去重.版本化备份的功能. 再次试用obnam 后(??乱痪洌核?故锹?某銎?,我注意到了它有一个moun

  • php编写一个简单的路由类 2014-06-10

    php编写一个简单的路由类,需要的朋友可以参考下. 类代码: <?php class Router { public function getRouter($types = 1) { if ( isset($_SERVER['PATH_INFO']) ) { $query_string = substr(str_replace(array('.html','.htm', '.asp', '//'), '',$_SERVER['PATH_INFO']),1); } else { $query_st

  • JQuery入门-编写一个简单的JQuery应用案例 2014-07-20

    首先引入JQuery文件库只需将文件导入页面中即可,即在<head></head>中,接下来详细介绍,感兴趣的朋友可以了解下 一.官方网站下载:http://jquery.com 二.引入JQuery文件库 下载完后不用安装,只需将文件导入页面中即可,即在<head></head>中加入如下代码:<script language="javascript" type="text/javascript" src=&q

  • php 5.6版本中编写一个PHP扩展的简单示例 2014-11-09

    这篇文章主要介绍了php 5.6版本中编写一个PHP扩展的简单示例,本文给出扩展实现代码.编译方法.配置方法和使用例子等内容,需要的朋友可以参考下 有时候在php本身没有满足需求的api时候,需要自己写相应的扩展,扩展写完之后进行编译,即可加入自己的开发环境中,扩展php的功能. 这里实现一个连接字符串和int型数的连接操作的简单扩展. 首先,下载最新的php源码安装包,进入ext/目录,新建extstrcat.def: string extstrcat(string strarg, int i

  • 用Python编写一个简单的Lisp解释器的教程 2015-02-22

    这篇文章主要介绍了用Python编写一个简单的Lisp解释器的教程,Lisp是一种源码简单的函数式编程语言,本文主要介绍对其中的一个子集Scheme的解释器开发,需要的朋友可以参考下 本文有两个目的: 一是讲述实现计算机语言解释器的通用方法,另外一点,着重展示如何使用Python来实现Lisp方言Scheme的一个子集.我将我的解释器称之为Lispy (lis.py).几年前,我介绍过如何使用Java编写一个Scheme解释器,同时我还使用Common Lisp语言编写过一个版本.这一次,我的目

  • 编写一个简单的JavaScript模板引擎 2014-03-10

    随着Nodejs的流行,JavaScript在前端和后端都开始流行起来.有许多成熟的JavaScript模板引擎,例如Swig,既可以用在后端,又可以用在前端. 不过很多时候,前端模板仅仅需要简单地创建一个HTML片段,用Swig这种全功能模板有点大材小用.我们来尝试自己编写一个简单的前端模板引擎,实际上并不复杂. 在编写前端模板引擎代码之前,我们应该想好如何来调用它,即这个模板引擎的接口应该是什么样的.我们希望这样调用它: // 创建一个模板引擎: var tpl = new Template

  • 无中生有,如何编写一个最简单的jQuery插件. - 专业成就梦想 2014-01-08

    首先是最基础的,大多数乃至99%的jQuery插件都会用到的代码介绍一下,一共就2条. 1. extend()函数,参数是一个Object,为一个对象做扩展,添加一些方法或者属性.比如: jQuery.extend({ hello:function(){ alert("say hello"); } }); 这是为jQuery类本身扩展了新的方法hello,也可以通过$.fn.extend()为jQuery对象添加方法.在插件的一般开发中,都是添加到对象的.如果这还不能理解的话,可以把j

  • 用 C 语言编写一个简单的垃圾回收器 2014-09-23

    人们似乎认为编写垃圾回收机制是很难的,是一种只有少数智者和Hans Boehm(et al)才能理解的高深魔法.我认为编写垃圾回收最难的地方就是内存分配,这和阅读K&R所写的malloc样例难度是相当的. 在开始之前有一些重要的事情需要说明一下:第一,我们所写的代码是基于Linux Kernel的,注意是Linux Kernel而不是GNU/Linux.第二,我们的代码是32bit的.第三,请不要直接使用这些代码.我并不保证这些代码完全正确,可能其中有一些我 还未发现的小的bug,但是整体思路仍

  • [原创]第一个iOS应用程序 2013-05-27

    万事开头难.斗霜傲雪二十年,堂堂剑气尚寒.--<诗词三百首> 第一章 窗口与应用程序 在iOS应用程序中窗口(Window)是视图(View)的载体,每一个应用程序都至少有一个Window,一般而言也只有一个Window,在某些特定应用中会出现多个Window,本文暂不考虑多窗口的情况.Window是UIWindow类的一个实例,当应用程序启动时会创建这个窗口.当窗口显示出来后,一般来说,开发者就很少再会用到窗口. 在"iOS开发入门教程"一文中已经描述过创建一个iOS引用

  • 用 C# 编写一个停放在任务栏上的图标程序 2014-01-03

    用 C# 编写一个停放在任务栏上的图标程序 作者: 蔡世友 类别: C#/VB 日期: 2002-1-30 10:21:46 01-12-6 上午 10:53:11 -------------------------------------------------------------------------------- 引 言 C#语言是微软公司针对.Net平台才推出来的一门新语言,作为.Net平台的第一语言,它几乎集中了所有关于软件开发和软件工程研究的最新成果.其是当前第一个完全面向组件

  • 用Python编写一个基于终端的实现翻译的脚本 2014-06-03

    这篇文章主要介绍了用Python编写一个基于终端的实现翻译的脚本,代码基于Python2.x,需要的朋友可以参考下 为什么写这个程序,为什么不给这个程序配备gui?原因很简单,因为我是一个命令行控,Linux习惯了不习惯了鼠标,总觉得点着不如敲命令快,各位在看这篇文章就说明和本人有相同的爱好.这个用python写的翻译工具是通过google来实现的,由于google返回的数据不是很规范(或者说我没有找到规律),现在前三项能正常显示(源词,翻译结果,和汉语拼音).下面的词性和其他释义可能不同,见谅

  • 如何编写一个ASP类 2014-07-19

    前几天大佛写了"ASP设计模式",可能有些初学者或者刚刚接触ASP的朋友不一定完全看得明白,偶就整理了一下编写一个ASP类的方法,大部分是从网上找来的.希望对朋友们有帮助. <ASP设计模式>(作者 我佛山人): dispbbs.asp?boardID=20&ID=247879 首先ASP的类是由事件和方法(它们就是构成类的成员了)构成的,如果大家还没有接触过,可以先看看下面的说明: 在 Class 块中,成员通过相应的声明语句被声明为 Private(私有成员,只

  • 使用Python编写一个模仿CPU工作的程序 2015-04-04

    这篇文章主要介绍了使用Python编写一个模仿CPU工作的程序,包括简单的内存和输入输出的实现,本文中的例子需要一定的Python编程基础,是深入Python的实践,需要的朋友可以参考下 今天早上早些时候,在我的Planet Python源中,我读到了一篇有趣的文章"开发CARDIAC:纸板计算机(Developing upwards: CARDIAC: The Cardboard Computer)",它是关于名为Cardiac的纸板计算机的.我的一些追随者和读者应该知道,我有一个名