说明

《Node.js 台湾社群协作电子书》写的不错,但是关于express描述中的部分内容选取的不正确,关于express的get、post、ajax三部分内容讲的其实是使用纯node代码实现的。这么好的一本教程,少了如今大行其道的express的部分内容,总感觉像是武功秘籍有残缺一样,甚是遗憾。所以,为了将教程实现的更完整,我特意花了一天的时间来完善express部分的内容。 但是本人水平毕竟有限,难免有疏漏之处,如果不当之处还请各位高人斧正,在这里提前谢过了。本文文档和代码都已经发布到了github上,如果想看代码的可以直接在这里https://github.com/yunnysunny/expressdemo找到。

6.6 模板引擎

express作为一个mvc框架,肯定不能仅仅是处理静态页,作为MVC中的V(视图),是其的有机组成部分。谈到视图,则不得不谈模板引擎,C(控制器)处理完请求后需要将处理后的数据发挥给视图层,这就没法回避从控制器中传递参数到视图层的问题,而在视图层解析这些参数正式模板引擎所要完成的任务。express中是没有内置的模板引擎的,他所使用的都是第三方的模板引擎,比如ejs、jade等。 下面通过命令行来快速生成一个express项目: express -e ejs myapp 命令会在当前执行目录下创建一个myapp文件夹,进入myapp目录下,会发现我们熟悉的pagekage.json文件,很明显里面含有对于express、ejs依赖的说明,但是目录下却没有文件夹node_modules,所以需要运行 npm install 来安装所有所需的依赖。 接着打开文件夹中的app.js,会发现生成的代码如下:

/**
 * Module dependencies.
 */

var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');

var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

app.get('/', routes.index);
app.get('/users', user.list);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
}); **代码片段 6.6.1** 注意这两行:

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

它代表使用的模板引擎为ejs,并且把所有的模板文件都放到了当前文件夹下的views目录中。 然后看一下:

app.use(express.static(path.join(__dirname, 'public')));

这代表将静态页放到了当前文件夹下的public目录中。 最后看一下路由设置:

app.get('/', routes.index);

我们找到routes.index文件的定义(位于routes目录下index.js文件中):

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

这里面遇到了一个render函数,这个函数就是express中用于加载模板的函数。通过代码也可以大体看出,第一个参数是模板的名字,它所代表的文件位于视图文件夹views目录下的index.ejs(ejs文件后缀是ejs模板引擎的默认后缀);而第二个参数即为传递给这个模板的参数。 接着看一下在模板中,是怎样使用刚才传递的那个titile参数的,打开views文件夹下的index.ejs:

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
  </body>
</html>

可以看到使用<%=titile%>的方式就可以把之前render函数中传递的title参数读取出来。 扩展一下在ejs中还有两个常见的标签: <%- %>:读取变量中的值且对于变量中的html特殊符号(比如<、>、&、”等)不进行转义,如果使用<%=%>就会把特殊符号转义, <%%>:写在这个标签里的语句会直接当成代码来解析,比如说如下代码:

<% if (status == 0) { %>
<input  type=”button” value=”启用” />
<% } else { %>
<input  type=”button” value=”禁用” />
<% } %>

6.7 Express 中的GET和POST

接下来的内容来讲一下express中怎样使用get和post,首先我们在views文件夹下新建目录user,然后在user目录下新建文件sign.ejs(当然你也可以把它当成静态页,放到public中;但是正常环境下,对于html一般都是通过视图的方式来加载)。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Node.js注册演示</title>
</head>
<body>
<h1>注册</h1>
<form id="signup" method="get" action="/users/do/sign">
<label>   帐号:</label><input type="text" name="username" />
<label>   Email:</label><input type="text" name="email" />
<input type="submit" value="注册" /><br>
</form>
</body>
</html>

代码6.7.1 sign.ejs代码

这里表单method是get(虽然一般情况下网服务器添加数据都是用post方式,但是这里为了演示方便,现将其写成get)。接下来看一下express中怎样在GET方式下获取表单中的数据。 为了演示用户注册这个流程,我们在routes/user.js中添加两个方法:

exports.showSign = function(req, res) {
	res.render('user/sign');
}

exports.doSign = function(req, res) {
	var name = req.query.name;
	var email = req.query.email;
	res.send('恭喜' + name +'注册成功,你的邮箱为:'+email);
}

代码6.7.2 新增user.js文件中处理函数

然后在app.js中添加相应的路由如下:

app.get('/users/sign', user.showSign);
app.get('/users/do/sign', user.doSign);

代码6.7.3 新增app.js中路由配置

运行node app.js,即可查看效果,打开http://localhost:3000/users/sign ,可看到如下界面:

注册显示界面

输入数据后,点击注册,显示提示信息:

注册成功显示界面

这就完成了get操作,但是前面提到了类似于这种注册操作一般都是用post的,将上面的代码改成post是很简单的,只需在代码代码6.7.1 中将表单的method改成post,代码6.7.2中获取请求数据是这么写的:

var name = req.query.name;
var email = req.query.email;

如果改成post,只需将其改为

var name = req.body.name;
var email = req.body.email;

6.8 Express AJAX 应用示例

还是上面的例子,只不过这次换成用ajax来提交数据,我们在views/user文件夹下再新建文件sign2.ejs:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Node.js注册演示</title>
<script language="javascript" src="/javascripts/jquery-1.10.2.js"></script>
</head>
<body>
<h1>注册</h1>
<form id="signup" method="post" action="/users/sign2">
<label>帐号:</label><input type="text" name="name" /><br />
<label>Email:</label><input type="text" name="email" /><br />
<input type="submit" value="注册" /><br>
</form>
<script language="javascript">
	$(document).ready(function() {
		$('#signup').submit(function() {
			$.post($(this).attr('action'),$(this).serialize(),function(result) {
				if (result.code == 0) {
					alert('注册成功');
				} else {
					if (result.msg) {
						alert(result.msg);
					} else {
						alert('服务器异常');
					}
				}
			},'json')
			return false;
		});
	});
</script>
</body>
</html>

代码6.8.1 sign2.ejs

为了使用ajax,我们引入了jquery,并将jquery-1.10.2.js放到了public/javascripts文件夹下,为了演示ajax和普通请求处理的区别,这里仅仅给出处理post请求的代码:

exports.doSign2 = function(req, res) {
	var name = req.body.name;
	var result = {};
	if (!name) {
		result.code = 1;
		result.msg = '账号不能为空';
		res.send(result);
		return;
	}
	var email = req.body.email;
	if (!email) {
		result.code = 2;
		result.msg = '邮箱不能为空';
		res.send(result);
		return;
	}
	res.send({code : 0});
}

代码6.8.2 ajax后台处理代码

express中res的send函数中传一个json对象,则发送给浏览器的时候会自动序列化成json字符串。