Express 入门

原文:Introduction to Express
笔记:涂鸦码龙

安装

package.json 中手动添加 Express 依赖

1
2
3
4
5
6
7
8
{
"name": "demo1",
"description": "First Express app",
"version": "0.0.1",
"dependencies": {
"express": "3.x"
}
}

相同的目录运行:

1
npm install

命令行安装 Express 直接运行:

1
npm install -g express

安装完后可以用 express 在命令行生成应用。

添加 app.js 文件,写个最简单的应用,创建一个 Express 实例,开始监听特定的端口 :

1
2
3
4
var express = require('express');
var app = express();
app.listen(3000);

运行 node app 没发生任何事情。

定义路由

我们给应用添加一些简单的路由,Express 可以响应各种 HTTP 请求:

1
2
3
4
5
6
7
8
//Regular HTTP get
app.get(some url, do something);
//Some other page
app.get(some other url, do something else);
//I can respond to a form post
app.post(some url, do more stuff);

我们写个真实的例子,给应用添加主页:

1
2
3
app.get('/', function(request, response) {
response.send("This would be some HTML");
});

注意 Express 为 response 对象添加了 send() 方法,一些模板代码可以处理响应。重启应用,访问 http://localhost:3000/ 看看。

request.send() API 智能处理不同类型的数据,假如你想为网站添加简单的基于 JSON 的 API ,Express 可以把返回结果转换成 JSON 并设置适当的响应头。

1
2
3
app.get('/api', function(request, response) {
response.send({name:"Raymond",age:40});
});

可想而知,添加更多的路由,并处理所需的响应,就可以构建一个应用。

通用的博客应用

跳过 package.json 文件,因为除了 name 属性不一样,其它项都一样。来看 app.js 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.sendfile('./views/index.html');
});
app.get('/about', function(req, res) {
res.sendfile('./views/about.html');
});
app.get('/article', function(req, res) {
res.sendfile('./views/article.html');
});
app.listen(3000);

我们把 send 换成了 sendfile 。与其把大段的 HTML 字符串放到 app.js 文件里,不如增加三个路由,主页,关于页,文章页。

添加 HTML

主页:

HTML 代码

这里并没什么特别的,Express 将返回纯静态的 HTML 。关于页和文章页只不过把 title 和 h1 值改了。

由静态到动态

Express 支持各种模板引擎,express 命令行可以添加对 Jade,EJS,JSHTML 和 Hogan 的支持。根据 Express 文档,任何模版引擎只要符合特定的签名就可以生效。推荐从 consolidate.js 库(模版引擎集合)里面查找喜欢的模版引擎。

我是 Handlebars 的超级粉,我的许多客户端应用都用它,服务器端自然也少不了它。要用 Handlebars 还需要安装包装库 hbs

1
2
3
4
5
6
7
8
9
{
"name": "blog2",
"description": "Blog app",
"version": "0.0.1",
"dependencies": {
"express": "3.x",
"hbs":"*"
}
}

然后更新 app.js 使用此引擎:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var express = require('express');
var app = express();
var hbs = require('hbs');
app.set('view engine', 'html');
app.engine('html', hbs.__express);
app.get('/', function(req, res) {
res.render('index');
});
app.get('/about', function(req, res) {
res.render('about');
});
app.get('/article', function(req, res) {
res.render('article');
});
app.listen(3000);

为了使用 Handlebars ,我们通过 require 把 HBS 包装库引入,然后让 Express 使用它。
默认情况下,Handlebars 会解析包含了特定引擎扩展的文件,这里是 something.hbs 。但也可以使用 “view engine”指令告诉 Express 动态解析 HTML 文件。这样我的编辑器可以提供良好的代码校验和语法高亮。通过 app.engine 真正加载引擎。

最终路由用了新的 render 方法,Express 默认使用 views 文件夹,因此我们可以省略。由于 Express 记住了我们要解析的扩展名,所以它也可以省略。res.render('something') 实际等于告诉 Express 查找 views/something.html 。Express 基于模板引擎规则解析它,并返回到浏览器。

主页显示博客条目

我们可以连接到 MySQL 或者 Mongo,这里我们创建一个静态的数据集,取名 blog.js ,它提供获取一组条目和获取一个条目功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var entries = [
{"id":1, "title":"Hello World!", "body":"This is the body of my blog entry. Sooo exciting.", "published":"6/2/2013"},
{"id":2, "title":"Eggs for Breakfast", "body":"Today I had eggs for breakfast. Sooo exciting.", "published":"6/3/2013"},
{"id":3, "title":"Beer is Good", "body":"News Flash! Beer is awesome!", "published":"6/4/2013"},
{"id":4, "title":"Mean People Suck", "body":"People who are mean aren't nice or fun to hang around.", "published":"6/5/2013"},
{"id":5, "title":"I'm Leaving Technology X and You Care", "body":"Let me write some link bait about why I'm not using a particular technology anymore.", "published":"6/10/2013"},
{"id":6, "title":"Help My Kickstarter", "body":"I want a new XBox One. Please fund my Kickstarter.", "published":"6/12/2013"}];
exports.getBlogEntries = function() {
return entries;
}
exports.getBlogEntry = function(id) {
for(var i=0; i < entries.length; i++) {
if(entries[i].id == id) return entries[i];
}
}

我们还可以提供添加,编辑和删除,这里到此为止。再更新下 app.js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var express = require('express');
var app = express();
var hbs = require('hbs');
var blogEngine = require('./blog');
app.set('view engine', 'html');
app.engine('html', hbs.__express);
app.use(express.bodyParser());
app.get('/', function(req, res) {
res.render('index',{title:"My Blog", entries:blogEngine.getBlogEntries()});
});
app.get('/about', function(req, res) {
res.render('about', {title:"About Me"});
});
app.get('/article/:id', function(req, res) {
var entry = blogEngine.getBlogEntry(req.params.id);
res.render('article',{title:entry.title, blog:entry});
});
app.listen(3000);

我们用 require 引入数据集,如此可以调用它得到条目。

在主页,我们给 render API 传两个参数,参数是一个对象,包含 titleentries 属性。title 的值时字符串,但是 entries 调用 blogEngine API 。我们传的任何数据将对模版生效,主页模版需要调整。

主页模版代码

即使你从没用过 Handlebars ,仍旧能猜到这里的含义。#each 指令将循环一个数组,{{}} 表示从博客条目列表取到的参数。

创建一个布局

你肯定关心剩下的 HTML 怎么改,Express 用了模版引擎会自动支持布局。这意味着我可以创建通用的网站设计布局,Express 将利用它构造特定的页面。按照约定,它取名 layout.something ,“something”是你使用的特定扩展。我们用了 HTML ,它就是 layout.html

layout.html 代码

关于页面没什么亮点,我们跳过,下面看文章路由。URL 现在包含了标记 :id ,Express 可以创建动态的 URL ,用于对应请求参数。我们定义的链接像这样:
/article/

理论上,每一个博客条目需要一个路由,但是创建一个抽象的路由匹配这些请求效果更好。 bodyParser (这一特性来源于 Connect 框架,的确对我很有帮助,它不仅支持查询字符串,而且支持表单主体,几乎每个 Express 应用都会用到它。)

显示个别文章

由于 URL 结尾包含动态值,我们可以把它传给 blogEngine 对象,用得到的结果作为视图的变量。

article.html 文件

现在我们创建了一个真正动态,但是难看的应用,这是新主页:

这是特定的博客条目:

润色!

我们加点基本的样式,让应用变好看点。Express 提供了简洁的方式添加图像,JavaScript 库和样式表之类的静态资源,简单定义一个静态文件夹,任何请求将从这个文件夹查找文件。

1
app.use(express.static('public'));

此时,如果请求 /foo.css,public 文件夹存在 foo.css 文件,将返回它。我把 Bootstrap 和 jQuery 副本放到了 public 文件夹。

然后在我的 layout.html ,我可以引用这些资源。这是链接 bootstrap.css 的例子:

代码实例

Express 将自动检查 public 文件夹,可以有多个静态文件夹,甚至可以自定义 URL 前缀。最终效果是极好的。

主页:

文章页:

进阶?

延伸阅读: