基于Node.js平台,快速、开放、极简的web开发框架。
$ npm install --save express
快速入门
安装
# 创建目录并进入此目录将其作为当前工作目录
$ mdkri app && cd app
# 为应用创建package.json文件,默认应用入口点文件(entry point)为index.js,可修改为app.js。
$ npm init
$ cat package.json
{
"name": "demo",
"version": "1.0.0",
"description": "express demo",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "junchow",
"license": "MIT"
}
# 安装Express并保存到依赖列表中,即将模块添加到package.json中dependencies依赖列表中。
$ npm install express --save
# 临时安装Express不添加到依赖列表中
$ npm install express
案例
# 创建入口点(entity point)文件
$ vim app.js
var express = require('express');
var app = express();
app.get('/', function(req,res){
res.send('hello');
});
var server = app.listen(3001, function(){
var host = server.address().address;
var port = server.address().port;
console.log("app listening at http://%s:%s", host, port);
});
# 启动应用
$ node app.js
# 浏览器访问
http://127.0.0.1:3001
应用生成器
使用Express应用生成器(脚手架)可快速构建应用的骨架
# 全局安装Express应用生成器
$ npm install express-generator -g
# 查看Express应用生成器所支持的命名行选项
$ express -h
# 查看Express版本
$ express --version
4.15.5
# 当前工作目录下创建名为app的应用
$ express app
# 进入app应用并安装依赖
$ cd app && npm install
# 设置debug模式启动app应用,默认服务监听端口为3000.
# Linux
> set DEBUG=app && npm start
# Windows
$ DEBUG=app npm start
# 浏览器访问
http://127.0.0.1:3000
基础路由
路由(Routing)是由一个URI(路径)和特定HTTP方法(GET/POST/...)组成,涉及到应用如何响应客户端对某网络节点的访问。
每个路由可有一个或多个处理器函数,当路由匹配时函数将被触发执行。
路由的定义结构
app.METHOD(PATH, HANDLER)
- app Express实例
- METHOD HTTP请求方式
- PATH 服务端路径
- HANDLER 当路由匹配时需执行的处理函数
实例
app.get('/', function(req, res){
res.send('Got a GET request');
});
app.post('/', function(req, res){
res.send('Got a POST request');
});
app.put('/', function(req, res){
res.send('Got a PUT request');
});
app.delete('/user', function(req, res){
res.send('Got a DELETE request at /user');
});
静态资源
Express内置的express.static
中间件可托管静态文件,例如CSS、JS、图片等文件。
将静态资源文件所在目录作为参数传递给express.static
中间件,即可提供静态资源文件的访问。
# 设置public目录作为静态资源存放目录
app.use(express.static('public'));
设置完成后即可直接通过URL访问静态资源文件
http://127.0.0.1:3000/css/bootstrap.css
http://127.0.0.1:3000/js/jquery.js
...
若静态资源存放在多个目录下面,则可多次调用express.static
中间件。
app.use(express.static('public'));
app.use(express.static('static'));
访问静态资源文件时,express.static
中间件会根据目录添加的顺序查找所需文件。
若希望通过express.static
中间件访问的文件都存在一个"虚拟(virtual)"目录(即目录根本不存在)下面,可通过为静态资源目录指定一个挂载路径的方式来实现。
app.use('/static', express.static('public'));
访问方式
http://127.0.0.1:3000/static/js/jquery.js
http://127.0.0.1:3000/static/css/bootstrap.css
使用指南
路由
路由指如何定义应用的端点(URIs)以及如何响应客户端请求。
路由是由URI、HTTP请求和若干句柄组成,其结构如下
app.METHOD(PATH, [callback], callback)
- app Express对象实例
- METHOD HTTP请求方式
- PATH 服务端路径
- callback 路由匹配时执行的处理函数
示例
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('hello');
});
路由方法
路由方法源于HTTP请求方法,和Express实例相关联。
app.get('/', function(req, res){
res.send('Got a GET request to the homepage');
});
app.post('/', function(req, res){
res.send('Got a POST request to the homepage');
});
app.all()
是一个特殊的路由方法,没有任何HTTP方法与之对应,其作用是相对于下一个路径上的所有请求加载中间件。
# 来自“/secret”的请求,任何HTTP请求,句柄都将执行。
app.all('/secret', function(req, res, next){
console.log('Accessing the secret section...');
next();//pass control to the next handler
});
请求路径
路由路径和请求一起定义定义了请求的端点,它可以是字符串、字符串模式、正则表达式。但是,查询字符串不是路由的一部分。
字符串模式路由路径
//匹配 /acd 和 /bcd
app.get('/ab?cd', function(req, res){
res.send('ab?cd');
});
//匹配/abcd、/abbcd、/abbbcd...
app.get('/ab+cd', function(req, res){
res.send('ab+cd');
});
//匹配/abcd、/ab1cd、/ab1212121dafacd...
app.get('/ab*cd', function(req, res){
res.send('ab*cd');
});
//匹配/abe 和 /abcde
app.get('/ab(cd)?e', function(req, res){
console.log('ab(cd)?e');
});
正则表达式路由路径
// 匹配任何路径中含有a的路径
app.get(/a/, function(req, res){
console.log('/a/');
});
// 匹配/butterfly、/dragonfly,不匹配/buterflyman、/dragonfly man...
app.get(/.*fly$/, function(req, res){
console.log('/.*fly$/');
});
路由句柄
路由句柄为请求处理提供多个回调函数,其行为类似中间件。
路由句柄与中间件的唯一区别在于回调函数有可能调用next('route')
方法而略过其他路由回调函数。
可利用此机制为路由定义前提条件,若现有路径上继续执行无意义,则可将控制权交给剩下的路径。
路由句柄形式包括一个函数、一个函数数组、两者混用。
# 使用一个回调函数处理路由
app.get('/user/create', function(req, res){
res.send('hello from user create');
});
# 使用多个回调函数处理路由,需指定next对象。
app.get('/user/create', function(req, res, next){
console.log('response will be send by the next function...');
next();
}, function(req, res){
res.send('hello from user create');
});
# 使用回调函数数组处理路由
app.get('/user/create', [callback1, callback2, callback3]);
var callback1 = function(req, res, next){
console.log('callback1');
next();
};
var callback2 = function(req, res, next){
console.log('callback2');
next();
};
var callback3 = function(req, res){
res.send('hello from user create');
};
# 混合使用函数和函数数组方式处理路由
app.get('/user/create', [callback1, callback2], function(req, res, next){
console.log('response will send by the next function...');
next();
}, function(req, res){
res.send('hello from user create');
})
响应方法
响应对象(res)的方法是向客户端返回响应,终结请求响应的其你去。若路由句柄中一个方法也不调用,癞子客户端的请求会一直挂起。
res.download() 提示下载文件
res.end() 终结响应处理流程
res.json() 发送一个JSON格式的响应
res.jsonp() 发送一个支持JSONP的JSON格式的响应
res.redirect() 重定向请求
res.render() 渲染视图模板
res.send() 发送各种类型的响应
res.sendFile() 以八位字节流形式发送文件
res.sendStatus() 设置响应状态码,并将其以字符串形式作为响应的一部分发送。
app.router()
可使用app.router()
创建路由路径的链式路由句柄,由于路径在一个地方指定,有助于创建模块化的路由,减少代码冗余和拼写错误。
# 定义链式路由句柄
app.route('/user')
.get(function(req, res){
res.send('Got a GET request from user');
})
.post(function(req, res)){
res.send('Got a POST request from user create');
}
.put(function(req, res){
res.send('Got a PUT request from user update');
});
express.Router
使用express.Router
类创建模块化、可挂载的路由句柄。
Router实例是一个完整的中间件和路由系统,因此又称之为"mini-app"。
$ vim tmp.js
var express = require('express');
var app = express();
//路由使用timelog中间件
router.use(function timelog(req, res, next){
console.log("time: ", Date.now());
});
// 定义路由
router.get('/', function(req, res){
res.send('homepage');
});
router.get('/user', function(req, res){
res.send('user');
});
module.exports = router;
# 应用中加载路由模块
var tmp = require('./tmp');
app.use('/tmp', tmp);
中间件
中间件(middleware)是一个函数,可访问请求对象(request object)、响应对象(response object),和web应用中处于请求-响应循环流程中的中间件,一般被命名为next的变量。
Express是一个自身功能极简,完全是由路由和中间件构成的一个web开发框架。
从本质上来说,一个Express应用就是在调用各种中间件。
中间件的功能包括
- 执行任何代码
- 修改请求和响应对象
- 终结请求-响应循环
- 调用堆栈中的下一个中间件
若当前中间件没有终结请求-响应循环,则必须调用next()
方法将控制权交给下一个中间件,否则请求就会挂起。
中间件可分为
- 应用级中间件
- 路由级中间件
- 错误处理中间件
- 内置中间件
- 第三方中间件
应用级中间件
应用级中间件可绑定到app
对象,使用app.use()
和app.METHOD()
,其中METHOD
是需要处理的HTTP请求的方法,例如GET、POST、PUT等。
var express = require('express');
var app = express();
// 没有挂载路由的中间件,应用的每个请求都会执行该中间件。
app.use(function(req, res, next){
console.log("time: ", Date.now());
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("reqeuest url: ", req.originalUrl);
next();
}, function(req, res, next){
console.log("request type: ", req.method);
next();
});
作为中间件系统的路由句柄,使得为路径定义多个路由成为可能。
//一个中间件栈,处理指向/user/:id的GET请求
app.get("/user/:id", function(req, res, next){
console.log("id:", req.params.id);
next();
}, function(req, res, next){
res.send("user info");//终止请求-响应循环
});
//处理/user/:id打印出用户id,不会被调用,因为第一个路由已经终止了请求-响应循环。
app.get("/user/:id", function(req, res, next){
res.end(req.params.id);
});
若需要在中间件栈中跳过剩余中间件,调用next('router')
方法将控制权交给下一个路由。
next('router')
只对使用app.VERB()
或router.VERB()
加载的中间件有效。
//一个中间件栈,处理指向/user/:id的GET请求
app.get('/user/:id', function(req, res, next){
//若user_id为0则跳到下一个路由,否认将控制权交给栈中下一个中间件。
if(req.params.id==0){
next('router');//在中间件栈中跳过剩余中间件,调用next('router')方法将控制权交给下一个路由。
}else{
next();
}
}, function(req, res, next){
res.render('regular');//渲染常规页面
});
//处理/user/:id渲染一个特殊页面
app.get('/user/:id', function(req, res, next){
res.render('special');
});
路由级中间件
路由级中间件和应用级中间件一样,只是它绑定的对象为 express.Router()
。
var router = express.Router();
路由级使用 router.use()
或 router.VERB()
加载。
var express = require('express');
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("requst type: ", req.method);
next();
});
//将路由挂载到应用
app.use('/', router);
错误处理中间件
# 错误处理中间件和其他中间件定义类似,仅需使用4个参数,其签名是(err,req,res,next)
app.use(function(err, req, res, next){
console.error(err.stack);
res.status(500).send('something broken');
});
错误处理中间件必须要有4个参数,定义错误处理中间件时必须使用这4个参数。即时无需next对象也必须在签名中声明它,否则中间件会被识别为一个常规中间件,无法处理错误。
系统内置中间件
从Express4.x开始,已不在依赖Connect。除了express.static
外,Express以前内置中间件现已全部单独作为模块安装使用。
express.static(root, [options])
express.static
是Express唯一内置的中间件,它基于serve-static
负责在Express应用中提供托管静态资源。
第三方中间件
$ npm install cookie-parse
$ vim app.js
var express = require('express');
var app = express();
// 加载第三方中间件cookie-parser
var cookieParser = require('cookie-parser');
// 记载用于解析cookie的中间件
app.use(cookieParser());