Express (Routing、Middleware、托管静态文件、view engine 等等)

1. Express 简介

Express 是基于 Node.js 平台,快速、开放、极简的 web 开发框架,它提供一系列强大的特性,帮助你建立各类 Web 和移动设备应用。css

Express 不对 Node.js 已有的特性进行二次抽象,咱们只是在它之上扩展了 Web 应用所需的基本功能。html

 

Express 是一个自身功能极简,彻底是由路由和中间件构成一个的 web 开发框架:从本质上来讲,一个 Express 应用就是在调用各类中间件。node

API 方面:丰富的 HTTP 快捷方法和任意排列组合的 Connect 中间件,让你建立健壮、友好的 API 变得既快速又简单。git

特性:github

  • Robust routing
  • Focus on high performance
  • Super-high test coverage
  • HTTP helpers (redirection, caching, etc)
  • View system supporting 14+ template engines
  • Content negotiation
  • Executable for generating applications quickly
 
Express
Express

安装和 hello world

--save:安装模块时,若是指定了 --save 参数,那么此模块将被添加到 package.json 文件中 dependencies 依赖列表中。 而后经过 npm install 命令便可自动安装依赖列表中所列出的全部模块。若是只是临时安装,不想将它添加到依赖列表中,只需略去 --save 参数便可web

踩坑:我在安装的时候,建立了一个叫 express 的项目文件夹,初始化以后,安装 express,出错(Refusing to install express as a dependency of itself)。缘由是,项目文件夹的名字和所要安装的项目依赖重名了,其实此时 package.json 文件中的名字也是 express,这样会出错,不能同名,因此要删除一切重来。正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
# 工做目录
$ mkdir myapp
$ cd myapp
 
# 经过 npm init 命令为你的应用建立一个 package.json 文件
$ npm init
 
# 应用的入口文件
entry point: app.js
 
# 安装 Express 并将其保存到依赖列表中
$ npm install express --save

下面的代码启动一个服务并监遵从 3000 端口进入的全部链接请求。他将对全部 (/) URL 或 路由 返回 hello world 字符串。对于其余全部路径所有返回 404 Not Found。express

req (request,请求) 和 res (response,响应) 与 Node 提供的对象彻底一致,所以,你能够调用 req.pipe()req.on('data', callback) 以及任何 Node 提供的方法。npm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const express = require( 'express');
 
let app = express();
 
app.get( '/', function (req,res) {
res.send( 'hello world');
});
 
let server = app.listen( 3000, function () {
let host = server.address().address;
let port = server.address().port;
 
console.log( `app listening at port: ${port}`);
});
1
2
3
4
# 启动此应用
$ node app.js
 
http://localhost: 3000/

Express 应用生成器

经过应用生成器工具, express 能够快速建立一个应用的骨架。json

经过 Express 应用生成器建立应用只是众多方法中的一种,你能够根据本身的需求修改它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 安装
$ npm install express-generator -g
 
# 列出全部可用命令
$ express -h
 
# 初始化一个 express 项目
$ express myapp
$ cd myapp
 
# 而后安装全部依赖包
$ npm install
 
# 启动此应用
$ npm start
 
http://localhost:3000/

2. 路由(Routing)

http://www.expressjs.com.cn/guide/routing.html

路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。

路由(Routing)是由一个 URI(路径)和一个特定的 HTTP 方法(get、post、put、delete 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。

结构

路由的定义由以下结构组成:

  • app 是一个 express 实例
  • method 是某个 HTTP 请求方式中的一个,Express 定义了以下和 HTTP 请求对应的路由方法: get, post, put, head, delete, options, trace, copy, lock, mkcol, move, purge, propfind, proppatch, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch, search, connect。有些路由方法名不是合规的 JavaScript 变量名,此时使用括号记法,好比:app['m-search']('/', function ...
  • path 是服务器端的路径
  • handler 每个路由均可以有一个或者多个处理器函数,当匹配到路由时,这些函数将被执行。
1
app.method(path, [ handler...], handler)
1
2
3
4
// respond with "hello world" when a GET request is made to the homepage
app.get( '/', function (req, res) {
res.send( 'Hello World!');
});

app.all() 是一个特殊的路由方法,没有任何 HTTP 方法与其对应,它的做用是对于一个路径上的全部请求加载中间件。

在下面的例子中,来自 /secret 的请求,无论使用 GET、POST 或其余任何HTTP 请求,句柄都会获得执行。

1
2
3
4
5
app.all( '/secret', function (req, res, next) {
res.send( 'GET request to the secret section');
console.log( 'Accessing the secret section ...');
next(); // pass control to the next handler
});

路由路径 path

路由路径和请求方法一块儿定义了请求的端点,它能够是字符串字符串模式或者正则表达式

使用字符串的路由路径示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 匹配根路径的请求
app.get( '/', function (req, res) {
res.send( 'root');
});
 
// 匹配 /about 路径的请求
app.get( '/about', function (req, res) {
res.send( 'about');
});
 
// 匹配 /random.text 路径的请求
app.get( '/random.text', function (req, res) {
res.send( 'random.text');
});

使用字符串模式的路由路径示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 匹配 acd 和 abcd
app.get( '/ab?cd', function(req, res) {
res.send( 'ab?cd');
});
 
// 匹配 /abe 和 /abcde
app.get( '/ab(cd)?e', function(req, res) {
res.send( 'ab(cd)?e');
});
 
// 匹配 abcd、abbcd、abbbcd等
app.get( '/ab+cd', function(req, res) {
res.send( 'ab+cd');
});
 
// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
app.get( '/ab*cd', function(req, res) {
res.send( 'ab*cd');
});

使用正则表达式的路由路径示例:

1
2
3
4
5
6
7
8
9
// 匹配任何路径中含有 a 的路径:
app.get( /a/, function(req, res) {
res.send( '/a/');
});
 
// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
app.get( /.*fly$/, function(req, res) {
res.send( '/.*fly$/');
});

路由句柄 handler

Can’t set headers after they are sent.

能够为请求处理提供多个回调函数,其行为相似 中间件。惟一的区别是这些回调函数有可能调用 next(‘route’) 方法而略过其余路由回调函数。能够利用该机制为路由定义前提条件,若是在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。

路由句柄有多种形式,能够是一个函数、一个函数数组,或者是二者混合,以下所示.

使用一个回调函数处理路由:

1
2
3
app.get( '/example/a', function (req, res) {
res.send( 'Hello from A!');
});

使用多个回调函数处理路由(记得指定 next 对象):

1
2
3
4
5
6
app.get( '/example/b', function (req, res, next) {
console. log( 'response will be sent by the next function ...');
next();
}, function (req, res) {
res.send( 'Hello from B!');
});

使用回调函数数组处理路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var cb0 = function (req, res, next) {
console.log( 'CB0');
next();
}
 
var cb1 = function (req, res, next) {
console.log( 'CB1');
next();
}
 
var cb2 = function (req, res) {
res.send( 'Hello from C!');
}
 
app.get( '/example/c', [cb0, cb1, cb2]);

混合使用函数和函数数组处理路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var cb0 = function (req, res, next) {
console. log( 'CB0');
next();
}
 
var cb1 = function (req, res, next) {
console. log( 'CB1');
next();
}
 
app.get( '/example/d', [cb0, cb1], function (req, res, next) {
console. log( 'response will be sent by the next function ...');
next();
}, function (req, res) {
res.send( 'Hello from D!');
});

响应方法 res

下表中响应对象(res)的方法向客户端返回响应,终结请求响应的循环。若是在路由句柄中一个方法也不调用,来自客户端的请求会一直挂起。

 
 

app.route()

可以使用 app.route() 建立路由路径的链式路由句柄。因为路径在一个地方指定,这样作有助于建立模块化的路由,并且减小了代码冗余和拼写错误。

1
2
3
4
5
6
7
8
9
10
app.route( '/book')
.get( function(req, res) {
res.send( 'Get a random book');
})
.post( function(req, res) {
res.send( 'Add a book');
})
.put( function(req, res) {
res.send( 'Update the book');
});

express.Router

可以使用 express.Router 类建立模块化、可挂载的路由句柄。

下面的实例程序建立了一个路由模块,并加载了一个中间件,定义了一些路由,而且将它们挂载至应用的路径上。

在 app 目录下建立名为 birds.js 的文件,内容以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var express = require( 'express');
var router = express.Router();
 
// 该路由使用的中间件
router.use( function timeLog(req, res, next) {
console.log( 'Time: ', Date.now());
next();
});
// 定义网站主页的路由
router.get( '/', function(req, res) {
res.send( 'Birds home page');
});
// 定义 about 页面的路由
router.get( '/about', function(req, res) {
res.send( 'About birds');
});
 
module.exports = router;

而后在应用中加载路由模块,应用便可处理发自 /birds/birds/about 的请求,而且调用为该路由指定的 timeLog 中间件。

1
2
3
var birds = require( './birds');
...
app. use( '/birds', birds);

3. 中间件(Middleware)

http://www.expressjs.com.cn/guide/using-middleware.html

中间件列表:https://github.com/senchalabs/connect#middleware

Express 是一个自身功能极简,彻底是由路由和中间件构成一个的 web 开发框架:从本质上来讲,一个 Express 应用就是在调用各类中间件。

中间件(Middleware) 是一个函数,它能够访问请求对象(req),响应对象(res),和 web 应用中处于请求-响应循环流程中的中间件,通常被命名为 next 的变量。

中间件的功能包括:

  • 执行任何代码。
  • 修改请求和响应对象。
  • 终结请求-响应循环。
  • 调用堆栈中的下一个中间件。

注意:若是当前中间件没有终结请求-响应循环,则必须调用 next() 方法将控制权交给下一个中间件,不然请求就会挂起。

app.use

1
app. use([path], function)

Use the given middleware function, with optional mount path, defaulting to “/“.

一个中间件处理器,请求来了,让那些中间件先处理一遍

  • 没有挂载路径的中间件,应用的每一个请求都会执行该中间件
  • 挂载至 /path 的中间件,任何指向 /path 的请求都会执行它

中间件分类

Express 应用可以使用以下几种中间件:

  • 应用级中间件
  • 路由级中间件
  • 错误处理中间件
  • 内置中间件
  • 第三方中间件

1. 应用级中间件

应用级中间件绑定到 app 对象 使用 app.use()app.METHOD(), 其中, METHOD 是须要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,所有小写。例如:

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
26
27
var app = express();
 
// 没有挂载路径的中间件,应用的每一个请求都会执行该中间件
app.use( function (req, res, next) {
console.log( 'Time:', Date.now());
next();
});
 
// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它
app.use( '/user/:id', function (req, res, next) {
console.log( 'Request Type:', req.method);
next();
});
 
// 路由和句柄函数(中间件系统),处理指向 /user/:id 的 GET 请求
app.get( '/user/:id', function (req, res, next) {
res.send( 'USER');
});
 
// 一个中间件栈,对任何指向 /user/:id 的 HTTP 请求打印出相关信息
app.use( '/user/:id', function(req, res, next) {
console.log( 'Request URL:', req.originalUrl);
next();
}, function (req, res, next) {
console.log( 'Request Type:', req.method);
next();
});

2. 路由级中间件

路由级中间件和应用级中间件同样,只是它绑定的对象为 express.Router()。路由级使用 router.use()router.VERB() 加载。

上述在应用级建立的中间件系统,可经过以下代码改写为路由级:

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
26
27
28
29
30
31
32
33
34
35
36
37
var app = express();
var router = express.Router();
 
// 没有挂载路径的中间件,经过该路由的每一个请求都会执行该中间件
router.use( function (req, res, next) {
console.log( 'Time:', Date.now());
next();
});
 
// 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息
router.use( '/user/:id', function(req, res, next) {
console.log( 'Request URL:', req.originalUrl);
next();
}, function (req, res, next) {
console.log( 'Request Type:', req.method);
next();
});
 
// 一个中间件栈,处理指向 /user/:id 的 GET 请求
router.get( '/user/:id', function (req, res, next) {
// 若是 user id 为 0, 跳到下一个路由
if (req.params.id == 0) next( 'route');
// 负责将控制权交给栈中下一个中间件
else next(); //
}, function (req, res, next) {
// 渲染常规页面
res.render( 'regular');
});
 
// 处理 /user/:id, 渲染一个特殊页面
router.get( '/user/:id', function (req, res, next) {
console.log(req.params.id);
res.render( 'special');
});
 
// 将路由挂载至应用
app.use( '/', router);

3. 错误处理中间件

http://www.expressjs.com.cn/guide/error-handling.html

错误处理中间件和其余中间件定义相似,只是必需要使用 4 个参数(err, req, res, next)。即便不须要 next 对象,也必须在签名中声明它,不然中间件会被识别为一个常规中间件,不能处理错误。

错误处理中间件应当在在其余 app.use() 和路由调用以后才能加载,好比:

1
2
3
4
5
6
7
8
9
10
var bodyParser = require( 'body-parser');
var methodOverride = require( 'method-override');
 
app. use(bodyParser());
app. use(methodOverride());
app. use( function(err, req, res, next) {
// 业务逻辑
console.error(err.stack);
res.status( 500).send( 'Something broke!');
});

中间件返回的响应是随意的,能够响应一个 HTML 错误页面、一句简单的话、一个 JSON 字符串,或者其余任何您想要的东西。

为了便于组织(更高级的框架),您可能会像定义常规中间件同样,定义多个错误处理中间件。好比您想为使用 XHR 的请求定义一个,还想为没有使用的定义一个,那么:

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
26
27
28
29
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
 
app. use(bodyParser());
app. use(methodOverride());
app. use(logErrors);
app. use(clientErrorHandler);
app. use(errorHandler);
 
// logErrors 将请求和错误信息写入标准错误输出、日志或相似服务:
function logErrors( err, req, res, next) {
console. error( err. stack);
next( err);
}
 
// clientErrorHandler 的定义以下(注意这里将错误直接传给了 next):
function clientErrorHandler( err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something blew up!' });
} else {
next( err);
}
}
 
// errorHandler 能捕获全部错误,其定义以下:
function errorHandler( err, req, res, next) {
res.status(500);
res.render(' error', { error: err });
}

4. 内置中间件

Express 之前内置的中间件如今已经所有单独做为模块安装使用了

express.static 是 Express 惟一内置的中间件,它基于 serve-static,负责在 Express 应用中提托管静态资源。

5. 第三方中间件

第三方中间件列表:http://www.expressjs.com.cn/resources/middleware.html

下面的例子安装并加载了一个解析 cookie 的中间件: cookie-parser

1
$ npm install cookie-parser
1
2
3
4
5
6
var express = require( 'express');
var app = express();
var cookieParser = require( 'cookie-parser');
 
// 加载用于解析 cookie 的中间件
app. use(cookieParser());

4. 托管静态文件

经过 Express 内置的 express.static 能够方便地托管静态文件,例如图片、CSS、JavaScript 文件等。

将静态资源文件所在的目录做为参数传递给 express.static 中间件就能够提供静态资源文件的访问了。

使用

全部文件的路径都是相对于存放目录的,所以,存放静态文件的目录名不会出如今 URL 中。

假设在 public 目录放置了图片、CSS 和 JavaScript 文件,你就能够:

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

如今,public 目录下面的文件就能够访问了。

若是你的静态资源存放在多个目录下面,你能够屡次调用 express.static 中间件:

访问静态资源文件时,express.static 中间件会根据目录添加的顺序查找所需的文件。

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

若是你但愿全部经过 express.static 访问的文件都存放在一个虚拟目录中(即目录根本不存在),能够经过为静态资源目录指定一个挂载路径的方式来实现,以下所示:

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

如今,你就爱能够经过带有 /static 前缀的地址来访问 public 目录下面的文件了。

__dirname

在任何模块文件内部,可使用__dirname变量获取当前模块文件所在目录的完整绝对路径。

1
console. log( __dirname);

path.join()

Arguments to path.join must be strings

将多个参数组合成一个 path,句法:

1
path .join( [path1], [path2], [...])

因为该方法属于path模块,使用前须要引入path 模块

1
2
3
4
5
6
7
8
const express = require( 'express');
const path = require( 'path');
 
var app = express();
 
app.set( 'views', path.join(__dirname, 'views'));
 
app. use(express. static(path.join(__dirname, 'public')));

5. 在 Express 中使用模板引擎

安装相应的模板引擎 npm 软件包,不须要在页面 require('pug');

1
$ npm install pug --save

编写模板文件

1
2
3
4
5
html
head
title!= title
body
h1!= message

而后建立一个路由,来渲染模板文件,模板文件会被渲染为 HTML。若是没有设置 view engine,您须要指明视图文件的后缀,不然就会遗漏它。

  • views 存放模板文件的目录
  • view engine 注册模板引擎
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
26
27
const express = require( 'express');
 
let app = express();
 
// view engine setup
app.set( 'views', [ './v']);
app.set( 'view engine', 'pug');
 
// http://localhost:3000/index
app.get( '/index', function(req, res) {
res.render( 'index', {
title: 'Hey',
message: 'view engine, Hello there!'
});
});
 
// 首页路由
app.get( '/', function (req,res) {
res.send( 'index routing, hello world');
});
 
let server = app.listen( 3000, function() {
let host = server.address().address;
let port = server.address().port;
 
console.log( `app listening at port: ${port}`);
});

6. Express 4 迁移

http://www.expressjs.com.cn/guide/migrating-4.html

Express 4 是对 Express 3 的一个颠覆性改变,也就是说若是您更新了 Express, Express 3 应用会没法工做。

1. 对 Express 内核和中间件系统的改进

Express 4 再也不依赖 Connect,并且从内核中移除了除 express.static 外的全部内置中间件。

也就是说如今的 Express 是一个独立的路由和中间件 Web 框架,Express 的版本升级再也不受中间件更新的影响。

移除了内置的中间件后,您必须显式地添加全部运行应用须要的中间件。请遵循以下步骤:

  1. 安装模块:npm install --save <module-name>
  2. 在应用中引入模块:require('module-name')
  3. 按照文档的描述使用模块:app.use( ... )

下表列出了 Express 3 和 Express 4 中对应的中间件:

 
 

2. 路由系统

应用如今隐式地加载路由中间件,所以不须要担忧涉及到 router 中间件对路由中间件加载顺序的问题了。

定义路由的方式依然未变,可是新的路由系统有两个新功能能帮助您组织路由:

  • 添加了一个新类 express.Router,能够建立可挂载的模块化路由句柄。
  • 添加了一个新方法 app.route(),能够为路由路径建立链式路由句柄。

3. 其余变化

 
 

运行

1
node .

迁移

卸载 Express 3 应用生成器:

1
$ npm uninstall -g express

而后安装新的生成器:

1
$ npm install -g express-generator
相关文章
相关标签/搜索