# Express.js
# 历史简介
2009 年 6 月 26 日,TJ Holowaychuk 提交了 Express 的第一次 commit,接下来在 2010 年 1 月 2 日,有 660 次 commits 的 Express 0.0.1 版本正式发布。TJ 和 Ciaron Jessup 是当时最主要的两个代码贡献者。在第一个版本发布的时候,根据 github 上的 readme.md,这个框架被描述成:
疯一般快速(而简洁)的服务端 JavaScript Web 开发框架,基于 Node.js 和 V8 JavaScript 引擎。
差不多 5 年的时间过去了,Express 拥有了 4,925 次 commit,现在 Express 的最新版本是 4.10.1,由 StrongLoop 维护,因为 TJ 现在已经跑去玩 Go 了>
# 官网
http://expressjs.com/en/index.html or http://expressjs.com/
中文网站:http://expressjs.com/zh/
# what is express?
Express 官网解释
Fast, unopinionated, minimalist web framework for Node.js A Node.js Framework Give Node.js structure Templating languages
- EJS
- Jade
Provide a routing mechanism For single page applitions Access to an MVC Pattern
- Model -- the Data
- View -- the Templete
- Controller -- the Javascript
# 特性
# Web Applications
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
Web 应用 Express 是一个简洁、灵活的 Node.js Web 应用开发框架, 它提供一系列健壮的特性,帮助你创建各种 Web 和移动设备应用。
# APIs
With a myriad of HTTP utility methods and middleware at your disposal, creating a robust API is quick and easy.
API 丰富的 HTTP 快捷方法随你信手拈来,让你创建健壮的 API 变得既快速又简单。
# Performance
Express provides a thin layer of fundamental web application features, without obscuring Node features that you know and love.
性能 Express 不对 Node 已有的特性进行二次抽象,我们只是在它之上扩展了 Web 应用所需的基本功能
# 运行原理
# 底层:http 模块
Express 框架建立在 node.js 内置的 http 模块上,http 模块生成服务器的原始代码如下:
var http = require("http");
var app = http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("Hello world!");
});
app.listen(3000, "localhost");
上面代码的关键是 http 模块的 createServer 方法,表示生成一个 HTTP 服务器实例。该方法接受一个回调函数,该回调函数的参数,分别为代表 HTTP 请求和 HTTP 回应的 request 对象和 response 对象。
上面的代码用 Express 改写如下:
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello world!');
});
app.listen(3000);
比较两段代码,可以看到它们非常接近。原来是用http.createServer方法新建一个app实例,现在则是用Express的构造方法,生成一个Epress实例。两者的回调函数都是相同的。
Express框架等于在http模块之上,加了一个中间层,Express框架的核心是对http模块的再包装
##中间件 ###什么是中间件 简单说,中间件(middleware)就是处理 HTTP 请求的函数。它最大的特点就是,一个中间件处理完,再传递给下一个中间件。App 实例在运行过程中,会调用一系列的中间件。
每个中间件可以从 App 实例,接收三个参数,依次为 request 对象(代表 HTTP 请求)、response 对象(代表 HTTP 回应),next 回调函数(代表下一个中间件)。每个中间件都可以对 HTTP 请求(request 对象)进行加工,并且决定是否调用 next 方法,将 request 对象再传给下一个中间件。
一个不进行任何操作、只传递 request 对象的中间件,就是下面这样。
function uselessMiddleware(req, res, next) {
next();
}
上面代码的 next 就是下一个中间件。如果它带有参数,则代表抛出一个错误,参数为错误文本。
function uselessMiddleware(req, res, next) {
next('some error');
}
抛出错误以后,后面的中间件将不再执行,直到发现一个错误处理函数为止。
###use()方法 use 是 express 注册中间件的方法,它返回一个函数。 例子:
var express = require("express");
var app = express();
app.use(function(request, response, next) {
console.log("In comes a " + request.method + " to " + request.url);
next();
});
app.use(function(request, response) {
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("Hello world!\n");
});
app.listen(1337);
上面代码使用 app.use 方法,注册了两个中间件。收到 HTTP 请求后,先调用第一个中间件,在控制台输出一行信息,然后通过 next 方法,将执行权传给第二个中间件,输出 HTTP 回应。由于第二个中间件没有调用 next 方法,所以 request 对象就不再向后传递了。
use 方法内部可以对访问路径进行判断,据此就能实现简单的路由,根据不同的请求网址,返回不同的网页内容。
app.use(function(request, response, next) {
if (request.url == "/about") {
response.writeHead(200, { "Content-Type": "text/plain" });
} else {
next();
}
});
除了在回调函数内部判断请求的网址,use 方法也允许将请求网址写在第一个参数。这代表,只有请求路径匹配这个参数,后面的中间件才会生效。无疑,这样写更加清晰和方便。
app.use('/path', someMiddleware);
上面代码表示,只对根目录的请求,调用某个中间件。
NOTE:
中间件的放置顺序很重要,等同于执行顺序。而且,中间件必须放在HTTP动词方法之前,否则不会执行
###set()方法 set 方法用于指定变量的值。 eg:
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
##路由&&http 方法 ###http 动词方法 针对不同的请求,Express 提供了 use 方法的一些别名。
- get
- post
- put
- ·····
- all(所有请求都必须通过该中间件)
- " * " (*表示对所有路径有效)
###Basic 路由
路由决定一个应用程序在某特定的端响应客户端的请求。这个端包括一个 URI(或路径)和一个具体的 HTTP 请求方法(GET,POST,等等)。
每一个路由有一个或多个处理函数,当路由被匹配时将会执行。
定义的路由会使用如下结构
app.METHOD(PATH, HANDLER)
app 是 express 的实例,METHOD 是 HTTP 请求方法,PATH 是请求服务器时的路径,HANDLER 是指当路由被匹配时所执行的函数。
eg:
// respond with "Hello World!" on the homepage
app.get('/', function (req, res) {
res.send('Hello World!');
})
// accept POST request on the homepage
app.post('/', function (req, res) {
res.send('Got a POST request');
})
// accept PUT request at /user
app.put('/user', function (req, res) {
res.send('Got a PUT request at /user');
})
// accept DELETE request at /user
app.delete('/user', function (req, res) {
res.send('Got a DELETE request at /user');
})
由于 get 方法的回调函数没有调用 next 方法,所以只要有一个中间件被调用了,后面的中间件就不会再被调用了
###Route methods 路由方法 A route method is derived from one of the HTTP methods, and is attached to an instance of the express class.
路由的方法派生自 HTTP 的方法,并且附加到一个 express 类的实例上
The following code is an example of routes that are defined for the GET and the POST methods to the root of the app.
以下的例子是一个以 app 为根的定义 GET 和 POST 方法的路由的例子
// GET method route
app.get('/', function (req, res) {
res.send('GET request to the homepage');
});
// POST method route
app.post('/', function (req, res) {
res.send('POST request to the homepage');
});
Express supports the following routing methods that correspond to HTTP methods: 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, and connect.
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, and connect.
To route methods that translate to invalid JavaScript variable names, use the bracket notation. For example, app['m-search']('/', function ...
There is a special routing method, app.all(), which is not derived from any HTTP method. This method is used for loading middleware functions at a path for all request methods.
有一个特殊的路由方法叫做 app.all(),该方法并不是从 HTTP 方法派生的,这个方法用于处理对指定 path 下的忽略访问方式的行为,也就是说:所有指定路径下的请求(不论是 get 还是 post 还是 delete)都必须通过该中间件。
In the following example, the handler will be executed for requests to “/secret” whether you are using GET, POST, PUT, DELETE, or any other HTTP request method
that is supported in the http module. (opens new window)
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...');
next(); // pass control to the next handler
});
###Route paths 路由路径 Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expressions.
路由路径,和请求方法的组合,定义了可以被请求的端点。路由的路径可以是
字符串,字符模式,或是正则表达式
Express uses path-to-regexp (opens new window) for matching the route paths; see the path-to-regexp documentation for all the possibilities in defining route paths. Express Route Tester (opens new window) is a handy tool for testing basic Express routes, although it does not support pattern matching.
Express 使用 path-to-regexp(一个 NPM 插件)去匹配路由路径,详情请见 path-to-regexp 的文档。Express Route Tester 是一个测试 express 路由的工具,但他不支持模式匹配
Query strings are not part of the route path.
##response 对象 ###redirect 方法 response.redirect 方法允许网址的重定向
response.redirect("/hello/anime");
response.redirect("http://www.example.com");
response.redirect(301, "http://www.example.com");
###sendFile 方法 response.sendFile 方法用于发送文件
response.sendFile("/path/to/anime.mp4");
###render 方法
response.render 方法用于渲染网页模板
app.get("/", function(request, response) {
response.render("index", { message: "Hello World" });
});
###setHeader 方法 如果需要指定 HTTP 头信息,回调函数就必须换一种写法,要使用 setHeader 方法与 end 方法
app.get('/', function(req, res){
var body = 'Hello World';
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Length', body.length);
res.end(body);
});
##request 对象 常用的 HTTP request 对象的属性和方法
属性或方法 | 说明 |
---|---|
ip | 请求的 IP 地址 |
path | 请求 URL 的路径部分 |
host | 请求的主机名 |
method | 请求的 http 方法 |
query | 请求的查询字符串部分 |
headers | 请求头的对象形式 |
##如何使用 ###安装
首先,为你的应用创建一个目录,然后进入此目录并将此目录作为工作目录。
$ mkdir myapp
$ cd myapp
在当前目录中创建一个 package.json 文件。可以通过 npm init 命令来完成此工作。
$ npm init
在当前目录中安装 Express 并将其添加到依赖列表中:
$ npm install express --save
如果只是希望临时安装 Express,不希望将其添加到依赖列表中,请略去 --save 命令行参数:
$ npm install express
或者你可以使用以下命令安装到全局本地环境中
$ npm install -g express --save
###创建项目 ####手动创建 安装好 Express 后,建立 app.js 文件 基本的实例代码如下:
var express = require('express')
var app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
var server = app.listen(3000, function () {
var host = server.address().address
var port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
此应用会启动一个服务并且监听 3000 端口。当访问主页时会返回“Hello World!”。 保存这段代码到一个名为 app.js 的文件,执行如下命令
$ node app.js
然后,在浏览器中访问 http://localhost:3000/查看结果。
或是设置到 package.json 中
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start":"node app.js"
}
使用 npm start 即可
####Express 应用生成器
使用应用生成器 express 快速创建一个应用程序框架。
你可以使用以下命令来安装:
$ npm install express-generator -g
使用 -h 选项来显示命令的选项列表:
$ express -h
Usage: express [options] [dir]
Options:
-h, --help output usage information 输出使用说明
-V, --version output the version number 输出版本号
-e, --ejs add ejs engine support (defaults to jade) 添加ejs引擎支持(默认为jade)
--hbs add handlebars engine support 添加handlebars引擎支持
-H, --hogan add hogan.js engine support 添加hogan.js引擎支持
-c, --css <engine> add stylesheet <engine> support (less|stylus|compass) (defaults to plain css) 添加样式表预处理引擎支持(less|stylus|compass)
-f, --force force on non-empty directory 强制在非空的目录生成应用框架
举个例子,下面的命令可以在当前目录创建一个名为 myapp 的 Express 应用。
$ express myapp
create : myapp
create : myapp/package.json
create : myapp/app.js
create : myapp/public
create : myapp/public/javascripts
create : myapp/public/images
create : myapp/routes
create : myapp/routes/index.js
create : myapp/routes/users.js
create : myapp/public/stylesheets
create : myapp/public/stylesheets/style.css
create : myapp/views
create : myapp/views/index.jade
create : myapp/views/layout.jade
create : myapp/views/error.jade
create : myapp/bin
create : myapp/bin/www
接下来安装依赖包:
$ cd myapp
$ npm install
运行程序 (MacOS 或 Linux):
$ DEBUG=myapp ./bin/www
在 Windows 上,你需要运行这条命令:
set DEBUG=myapp & node .\bin\www
接下来在浏览器中打开 http://localhost:3000/ 来访问应用。
生成的应用目录结构大概是这样的:
# 静态文件
对于使用静态文件,比如 images,CSS,JavaScrip 和其他静态文件,Express 中内置了中间件 - express.static
通过引入静态资源的路径,express.static 中间件可以获取该路径下的所有文件。 举例来说,如果你把 image,CSS,JavaScript 文件放入到一个名为 public 的文件夹,你可以这样做:
app.use(express.static('public'));
现在,你能够获取 public 目录下所有文件:
http://localhost:3000/images/kitten.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/images/bg.png
http://localhost:3000/hello.html
这些文件由于在相对位置被找到,所以静态路径的名字并不会出现在 URL 中。
如果你想使用多个路径来引用不同的静态资源,可以使用 express.static 中间件多次:
app.use(express.static('public'));
app.use(express.static('files'));
这些文件将会按照在静态目录中使用 express.static 中间件设置的顺序依次被查找到。
如果你想创建一个“虚拟”(这个路径在文件系统中并不存在)路径前缀,可以指定安装路径,如下所示:
app.use('/static', express.static('public'));
现在,你能够获取 public 目录下的所有文件,包括路径前缀“static/”
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html
# Express.Router 用法
从 Express 4.0 开始,路由器功能成了一个单独的组件Express.Router
。它好像小型的 express 应用程序一样,有自己的 use、get、param 和 route 方法。 ###基本用法
首先,Express.Router 是一个构造函数,调用后返回一个路由器实例。然后,使用该实例的 HTTP 动词方法,为不同的访问路径,指定回调函数;最后,挂载到某个路径。
var router = express.Router();
router.get('/', function(req, res) {
res.send('首页');
});
router.get('/about', function(req, res) {
res.send('关于');
});
app.use('/', router);
这种路由器可以自由挂载的做法,为程序带来了更大的灵活性,既可以定义多个路由器实例,也可以为将同一个路由器实例挂载到多个路径
# router.route 方法
router 实例对象的 route 方法,可以接受访问路径作为参数
var router = express.Router();
router.route('/api')
.post(function(req, res) {
// ...
})
.get(function(req, res) {
Bear.find(function(err, bears) {
if (err) res.send(err);
res.json(bears);
});
});
app.use('/', router);
# router 中间件
use 方法为 router 对象指定中间件,即在数据正式发给用户之前,对数据进行处理。下面就是一个中间件的例子。
router.use(function(req, res, next) {
console.log(req.method, req.url);
next();
});
上面代码中,回调函数的 next 参数,表示接受其他中间件的调用。函数体中的 next(),表示将数据传递给下一个中间件。
# 扩展使用
Routes and other application-specific logic can live in as many files as you wish, in any directory structure you prefer. View the following examples for inspiration:
- Route listings (opens new window)
- Route map (opens new window)
- MVC style controllers (opens new window)
上述的资源已 clone 到本地,详见
~/WorkSpace/Demo/express example
Also, there are third-party extensions for Express, which simplify some of these patterns:
# 常用的方法
# next()
此方法用于指定一个路由有一个中间件处理之后是否还需要其他中间件处理; 比如: 情景一:
app.get('/user', function(req, res) {
console.log('go into the user ');
res.send('Got a GET request at /user');
})
app.all('/user', function(req, res, next) {
console.log('Accessing the secret section ... 1st');
next(); // pass control to the next handler
});
会发现并没有输出 Accessing the secret section ... 1st 所以,也就是说,如果默认的不添加 next 参数,是不会向下传递的, 同理,如果添加了不调用也是不能起作用的,所以正确的方法是:
app.get('/user', function(req, res, next) {
console.log('go into the user ');
res.send('Got a GET request at /user');
next();
})
app.all('/user', function(req, res, next) {
console.log('Accessing the secret section ... 1st');
next(); // pass control to the next handler
});
# 简单的相对完整的例子
# FAQ
# How do I define models? 我怎样定义模型?
Express has no notion of a database. This concept is left up to third-party Node modules, allowing you to interface with nearly any database.
Express 没有数据库的概念,这个实现留给了第三方的 Node modules 模块,可以使用任何数据库的的接口
See LoopBack for an Express-based framework that is centered around models.
# How can I authenticate users? 怎样进行用户验证?
Authentication is another opinionated area that Express does not venture into. You may use any authentication scheme you wish. For a simple username / password scheme, see this example.
验证是一个有分歧的区域,express 不愿意冒险进入。你可以用自己喜欢的任意的方案,你可以参考这个例子 Eg: Authenticate users (opens new window)
###Which template engines does Express support?Express 支持哪一种模板引擎? Express supports any template engine that conforms with the (path, locals, callback) signature. To normalize template engine interfaces and caching, see the consolidate.js (opens new window) project for support. Unlisted template engines might still support the Express signature.
Express 支持任意一种符合 (path, locals, callback)签名的模板引擎,要标准化模板引擎接口和缓存,看 consolidate.js 项目。其中未列出的模板引擎
可能
仍然支持 Express 签名
###How do I handle 404 responses?怎样处理 404 响应? In Express, 404 responses are not the result of an error, so the error-handler middleware will not capture them. This behavior is because a 404 response simply indicates the absence of additional work to do; in other words, Express has executed all middleware functions and routes, and found that none of them responded. All you need to do is add a middleware function at the very bottom of the stack (below all other functions) to handle a 404 response:
在 Express 中,404 响应并不是一个错误的结果,所以错误处理中间件并不会捕获他们。这种现象是因为一个 404 响应简单地表明不存在的额外的工作要做,换句话说,Express 已执行所有中间件的功能和路线,发现他们没有回应。所有你需要做的是在栈底(在所有其他功能的底下)增加一个中间件的功能来处理 404 响应:
app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});
# How do I setup an error handler? 我怎么去设置一个错误处理
You define error-handling middleware in the same way as other middleware, except with four arguments instead of three; specifically with the signature (err, req, res, next):
定义一个错误处理的中间件和定义其他的中间件是一样的,只不过会有 4 个参数而不是 3 个,按照这样的签名(err, req, res, next)
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
# How do I render plain HTML?我怎么去渲染一个纯 HTML
You don’t! There’s no need to “render” HTML with the res.render() function. If you have a specific file, use the res.sendFile() function. If you are serving many assets from a directory, use the express.static() middleware function.
不需要使用 render 方法