Express入门

基于Node.js平台,快速、开放、极简的Web开发框架。

概述

express()用来创建一个Express的程序。express()方法是express模块导出的顶层方法。

1
2
var express = require('express');
var app = express();

原理

底层

Express框架是基于node.js内置的http模块。

1
2
3
4
5
6
const http = require('http')
const server = http.createServer((req, res) => {
res.end('hello world')
});

server.listen(8000, 'localhost')

Express框架是在http模块之上,加了中间层。

中间件

中间件是处理HTTP请求的函数。它最大的特点就是,一个中间件处理完,再传递给下一个中间件。每个中间件可以从App实例对象中接收三个参数,依次为req,res,next。每个中间件都可以对HTTP请求(req)进行加工,并且决定是否调用next方法,将req再传给下一个中间件。

express中使用use方法来注册中间件,它返回一个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var express = require('express');
var app = express();
var http = require('http')

//use同样可以匹配路径,实现路由功能,如 app.use('/root', () => {}),未加路径即可认为匹配'/'
app.use((req, res, next) => {
console.log('this is a')
next()
})

app.use((req, res, next) => {
res.end('hello world')
})
http.createServer(app).listen(8080, 'localhost')

use注册的中间件依次执行,通过next方法转移执行权,知道http响应请求。

顶级函数

express.static

express.static(path, [options])是Express中唯一的内建中间件,负责托管 Express 应用内的静态资源。

参数root为静态资源的所在的根目录。有关options参数的更多信息,请参见express.static

app.use(express.static(__dirname + ‘./public’))

express.Router

express.Router(options)是一个构造函数,用来创建路由实例,仅能够执行中间件和路由功能。使用该实例的http method方法,为不同路径指定回调函数,最后挂载在某个路径下。

1
2
3
4
5
6
7
var router = express.Router();

router.get('/', function(req, res) {
res.send('首页');
});

app.use('/', router); // 别遗忘了注册中间件

同样地,该方法支持all,get,put,post,delete等方法。

param

router.param(name, callback)将回调触发器添加到路由参数。

回调函数的参数为:

  • req,即请求对象。
  • res,即响应对象。
  • next,指示下一个中间件功能。
  • name参数的值。
  • 参数的名称。
1
2
3
4
5
6
7
8
9
10
11
// 当调用localhost:3000/user/123时触发param
router.param('id', function (req, res, next, id) {
console.log('CALLED ONLY ONCE')
next()
})
router.get('/user/:id', function (req, res) {
console.log('and this matches too')
res.end()
})
// CALLED ONLY ONCE
// and this matches too

route

router.route(path)返回单个路由的实例,然后您可以使用该路由使用可选的中间件来处理http method,支持链式调用。

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 router = express.Router()

router.param('user_id', function (req, res, next, id) {
req.user = {
id: id,
name: 'TJ'
}
next()
})

router.route('/users/:user_id')
.all(function (req, res, next) {
next()
})
.get(function (req, res, next) {
res.json(req.user)
})
.put(function (req, res, next) {
req.user.name = req.params.name
res.json(req.user)
})
.post(function (req, res, next) {
next(new Error('not implemented'))
})
.delete(function (req, res, next) {
next(new Error('not implemented'))
})

use/all

use类似app.use指定中间件函数。

all类似app.all匹配*路径,注册中间件。

app对象

all和http methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var express = require("express");
var http = require("http");
var app = express();
// *表示对所有路径都有效
app.all("*", (request, response, next) => {
response.writeHead(200, { "Content-Type": "text/plain" });
next();
});

app.get("/a", function(request, response) {
response.end("我获取了a页面的数据");
});

app.post("/a", function(request, response) {
response.end("我创建了a页面的一条数据");
});

app.put("/a", function(request, response) {
response.end("我修改了a页面的一条数据");
});

http.createServer(app).listen(8080, 'localhost')

除了all的全匹配和http method 的路径匹配,Express还支持模式匹配。

1
2
3
4
5
6
7
8
9
10
11
12
app.get("/detail/:id", function(req, res) {
res.end(`你查找的是id为${req.params.id}的数据`);
});

app.get('/forum/:fid/thread/:tid', middleware)
// 匹配/commits/71dbb9c
// 或/commits/71dbb9c..4c084f9这样的git格式的网址
app.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function(req, res){
var from = req.params[0];
var to = req.params[1] || 'HEAD';
res.send('commit range ' + from + '..' + to);
});

set/get

set方法用于指定变量的值,get方法用于获取指定变量的值。

1
2
3
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.get('view engine') // jade

locals

app.locals对象是一个javascript对象,它的属性就是程序本地的变量。一旦设定,app.locals的各属性值将贯穿程序的整个生命周期,与其相反的是 res.locals ,它只在这次请求的生命周期中有效。

1
2
3
app.locals.title = 'My App';
app.locals.strftime = require('strftime');
app.locals.email = 'me@myapp.com';

mountpath

app.mountpath属性是子程序挂载的路径模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require('express')
const app = express();
const child = express(); // 子程序

child.get('/', (req, res) => {
console.log(req.baseUrl); // /child
console.log(child.mountpath); // /child
res.send('child page')
})

app.use('/child', child)

app.listen(3333, () => {
console.log('已连接')
})

当访问localhost:3333/child路径时,页面输出child page,打印 /child;

它与req.baseUrl属性较为相似。但又不完全相似,req。baseUrl是输出URL路径,而mouthpath输出匹配模式。

如果一个子程序被挂载在多条路径模式,app.mountpath就是一个关于挂载路径模式项的列表,如下面例子所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const express = require('express')
const app = express();
const admin = express();
const secret = express();

admin.get('/', (req, res) => {
console.log('home', req.baseUrl);
console.log('admin', admin.mountpath);
res.send('admin home')
})

secret.get('/', (req, res) => {
console.log('secret', req.baseUrl);
console.log('secret', secret.mountpath);
res.send('admin secret')
})

admin.use('/secr*t', secret)
app.use(['/adm*n', '/manager'], admin)

app.listen(3333, () => {
console.log('已连接')
})

当访问/adm*n或/manager时,服务端控制台输出:

客户端输出:

当访问/adm*n/secr*t或/manager/secr*t时,客户端输出:

客户端输出:

res对象

redirect

res.redirect方法允许网址的重定向。

1
2
res.redirect('http://baidu.com')
res.redirect(301, '/login')

sendFile

res.sendFile方法用于发送文件。

1
res.sendFile(path.join(__dirname, 'package.json'))

render

res.render方法用于渲染网页模板。

1
2
3
4
res.render('index.html')
res.render('index', {
id: 1
})

res.cookie(name, value, options)设置cookie。

res.clearCookie(name)清除cookie。

download

res.cookie(path)下载文件。

sendStatus/status

res.sendStatus将响应HTTP状态代码设置为statusCode,并将其字符串表示形式发送为响应主体。

1
2
3
4
res.sendStatus(200) // equivalent to res.status(200).send('OK')
res.sendStatus(403) // equivalent to res.status(403).send('Forbidden')
res.sendStatus(404) // equivalent to res.status(404).send('Not Found')
res.sendStatus(500) // equivalent to res.status(500).send('Internal Server Error')

res.status设置响应的HTTP状态。

1
2
3
res.status(403).end()
res.status(400).send('Bad Request')
res.status(404).sendFile('/absolute/path/to/404.png')

set

将响应的HTTP标头设置fieldvalue。要一次设置多个字段,请传递一个对象作为参数。

1
2
3
4
5
6
7
res.set('Content-Type', 'text/plain')

res.set({
'Content-Type': 'text/plain',
'Content-Length': '123',
ETag: '12345'
})

别名为res.header(field [, value])

req对象

baseUrl

req.baseUrl实例安装所在的URL路径。

如果只有一个主程序,那么baseUrl输出’/‘,如果有多个子程序,参考app.mountpath示例

query

req.query用于获取请求参数params对象

body

req.body用于获取请求参数body对象

cookies

req.cookies用于获取cookies对象

https服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fs = require('fs');
var options = {
key: fs.readFileSync('E:/ssl/myserver.key'),
cert: fs.readFileSync('E:/ssl/myserver.crt'),
passphrase: '1234'
};

var https = require('https');
var express = require('express');
var app = express();

app.get('/', function(req, res){
res.send('Hello World');
});

https.createServer(options, app).listen(8080);
console.log('Server is running on port 8080');

上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var express = require('express');
var router = express.Router();
var multer = require('multer');

var uploading = multer({
dest: __dirname + '../public/uploads/',
// 设定限制,每次最多上传1个文件,文件大小不超过1MB
limits: {fileSize: 1000000, files:1},
})

router.post('/upload', uploading, function(req, res) {

})

module.exports = router

Events

app.on(‘mount’, callback(parent))

当子程序被挂载到父程序时,mount事件被发射。父程序对象作为参数,传递回调方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const express = require('express')
const app = express();
const admin = express(); // 子程序

app.locals.title = '123'

admin.on('mount', function(parent) {
// parent即app对象
console.log('Admin Mounted');
console.log(parent.locals.title); // refers to the parent app
});
admin.get('/', function(req, res) {
res.send('Admin Homepage');
});
app.use('/admin', admin);

app.listen(3333, () => {
console.log('已连接')
})

挂载时服务端输出Admin Mounted和123,访问/admin时,页面输出Admin Homepage