Express是建立在NODEJS http模块基础上的网络服务框架,具有以下几个特点:
- 使用JavaScript作为开发语言,实现前端后端语言的一致性
- 最大特点:中间件 和 路由
- 灵活性,由于采用模块化的理念,可以根据需求对服务进行组装
express的基本概念,可以参见express官网, 其核心内容很少。下面是自己学习的一些笔记。
一.第三方middleware库
morgan: LOGGING MIDDLEWARE
这个用于记录日志使用,可用于记录用户操作,服务端响应请求requests花费时间,用作性能分析。
安装:
npm install --save morgan
使用:
var express = require('express');
var http = require('http');
// 引入 Morgan 模块
var logger = require('morgan');
var app = express();
// 使用morgan中间件, 并规定输出格式
// 还可以设置为其它选项,比如 app.use(logger('dev'));
app.use(logger('short'));
app.use(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('hello express');
});
http.createServer(app).listen(3000);
// 登录 localhost:3000
// console中显示
::1 - GET / HTTP/1.1 200 - - 0.621 ms
express.static
这个是express自身封装的一个中间件,所以无需从npm中安装。这个中间件用于发送静态资源,比如images, CSS 或HTML文件等。
假如应用的静态资源存放在 public
目录下,则可以通过此中间件发送文件:
var path = require('path');
// 使用NODE的 path模块设置 public 路径
var publicPath = path.resolve(__dirname, 'public');
// 声明 'public' 目录为静态文件夹
app.use(express.static(publicPath));
app.use(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('没有找到该静态文件');
});
// ...
如果没有找到文件,则app将进入下一个中间件,如果找到了,express.static将发送该文件,并且停止中间件的链式操作。
值得之一的是,开发过程中,可以使用 res.sendFile()
提供静态文件,但是在生产环境中不要执行此函数,因为它必须针对每次文件请求读取文件系统,会产生严重的延迟,影响应用程序的总体性能。 建议使用 serve-static
中间件,这个中间件经过优化,用于为Express应用程序提供文件。 还有一个更好的选项,就是使用逆向代理提供静态文件。
其它
暂时先接触到这些中间件,其它的一些中间件仅作列举,以后碰到再详谈:
-
body-parser
: 解析HTTP request body -
connect-ratelimit
: 限制每小时连接数量,如果某人向服务器发送大量请求,这利用这个中间件返回错误,阻止服务器宕机 -
helmet
: 给应用添加 HTTP headers, 用于阻止特定类型的攻击 -
cookie-parser
: 解析浏览器cookies -
response-time
: 发送 X-Response-Time header,用于调试app性能
二.Routing
路由根据 URL 和特定的 HTTP method 将请求(requests)匹配到特定的处理函数(handlers).
例如:
var express = require('express');
var http = require('http');
var path = require('path');
var app = express()
var publicPath = path.resolve(__dirname, 'public');
app.use(express.static(publicPath));
// 对于 '/' 表示根目录
// HTTP GET 请求
app.get('/', function(req, res) {
res.end('这是主页');
});
// 动态的路径匹配 '/about/:name'
app.get('/about/:name', function(req, res) {
res.end(`${req.params.name} 的About页面`)
});
// 如果没有匹配到,则返回404
// 使用中间件
app.use(function(req, res) {
// 将状态码设置为 404
res.statusCode = 404;
res.end('没有找到该页面')
});
http.createServer(app).listen('3000');
除了可以使用 app.get(), 还可以使用其它的http动作,比如 app.post()等等。
路由的路径匹配有3中方式:
- 字符串
- 字符串模式: 其实就是正则表达式和字符串的一种结合写法
- 正则表达式
1.最常见的字符串:
app.get('/home', ...)
app.get('/about/company', ...)
app.get('/user/:id', ...) // 匹配 /user/1, /user/2, ....
2.字符串模式
app.get('/ab?cd', ...) // 匹配 acd, abcd
app.get('/ab+cd', ...) // 匹配 abcd, abbcd, abbb...cd
app.get('/ab(cd)?e',...) // 匹配 abe, abcde
3.正则表达式
app.get(/a/, ...) // 路径中所有具有 'a' 的路由
app.get(/.*fly$/, ...) // 任意以 'fly' 结尾的路由
三.request对象和response对象
这2个对象在Express中得到了扩充,在NODEJS中有些属性是没有的,下面罗列一些Express对这2个对象的扩充属性:
res.redirect([status,] path)
response对象添加了重定向方法,状态码默认为302
, 有以下几种形式:
res.redirect('/foo/bar')
res.redirect('http://example.com')
res.redirect(301, 'http://example.com')
res.redirect('..')
res.redirect('back')
# 1.完全匹配URL重定向到另一个网址
res.redirect('http://google.com');
# 2.相对host根目录,比如现在在 ' http://example.com/admin/post/new'
// 下面可以重定向到 'http://example.com/admin'
res.redirect('/admin')
# 3.可以相对当前目录,比如现在在 'http://example.com/blog/admin/'(注意最后有 '/')
// 下面可以重定向到 ' http://example.com/blog/admin/post/new'
res.redirect('post/new');
// 如果当前在 'http://example.com/blog/admin'(注意最后没有 '/')
// 下面可以重定向到 ' http://example.com/blog/post/new'
# 4.相对路径也是可能的,比如现在在 'http://example.com/admin/post/new'
// 下面可以重定向到 ' http//example.com/admin/post'
res.redirect('..')
# 5.'back' 返回到 referer; 如果referer不存在,默认为 '/'
res.redirect('back')
res.sendFile(path[, options][,fn])
Express v4.8.0版本以上支持此方法。
发送指定path的文件, path
必须是一个绝对路径。 Content-Type
根据文件的扩展设置。
options具体选项参见文档 sendFile()
fn
必须是通过终止request-response cycle或将控制传递给下一个route
app.get('/file/:name', function(req, res, next) {
var options = {
root: __dirname + '/public',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-send': true
}
};
var fileName = req.params.name;
res.sendFile(fileName, options, function(err) {
if (err) {
next(err);
} else {
console.log('Send: ' + fileName);
}
});
});
res.status(code)
设置响应的 HTTP status,返回值可链式调用
res.status(403).end()
res.status(400).send('BAD REQUEST')
res.status(404).sendFile('/absolute/path/to/404.html')
res.render(view[,locals][,callback])
渲染view,并且发送已渲染的html字符串到客户端,参数:
locals: 一个对象,其属性定义view中的本地变量
callback: 返回可能的错误和渲染的字符串,当发生错误,方法内部调用next(err)
view: 文件路径字符串。可以是绝对路径或相对 views 设置的相对路径。如果路径不包含文件扩展,则根据
view engine
设置来决定文件扩展
local variable cache 使view缓存。设置为true,则开发时将缓存。对产品阶段,view caching默认为true
// 发送渲染的view到客户端
res.render('index')
// 如果回调指定,则渲染的html字符串必须显式的发送
res.render('index', function(err, html) {
res.send(html);
});
// 传递一个local变量给view
res.render('user', { name: 'Tobi' }, function(err, html) {
// ...
})
req.get(field)
请求新增的方法,返回特定的 HTTP request header字段(不区分大小写)
req.get('Content-Type') // 'text/plain'
req.get('something') // undefined
req.ip
返回请求的ip地址
var EVIL_IP = '123.45.67.89';
app.use(function(req, res, next) {
if (req.ip === EVIL_IP) {
res.status(401).send('不许该ip访问');
} else {
next();
}
});
// ...
四.Views
express 的视图可以采用多种模版来渲染,比如EJS(Embedded JavaScript), Pug, Handlebars等。
下面方式来设置视图:
// 设置视图所在文件夹为 views
app.set('views', path.resolve(__dirname, "views"))
// 设置视图渲染引擎
// 这些引擎需要自己安装 npm install --save ejs
app.set('view engine', 'ejs')
五.示例
下面是留言本的一个小网页:
// app.js
var express = require('express');
var http = require('http');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var app = express();
// 设置视图文件夹和渲染引擎
app.set('views', path.resolve(__dirname, 'views'));
app.set('view engine', 'ejs');
var entries = [];
app.locals.entries = entries; // 使entries在所有的views中都可用
app.use(logger('dev'));
// 如果用户提交表单。产生一个 req.body 变量
// extended 选项是必须的
app.use(bodyParser.urlencoded({ extended: false }));
// 访问根路径,返回 views/index.ejs
app.get('/', function(req, res) {
res.render('index');
});
app.get('/new-entry', function(req, res) {
res.render('new-entry');
});
// 定义路由处理 '/new-entry' 下的 POST 请求
app.post('/new-entry', function(req, res) {
// 如果用户没有填写title或body,则返回400错误
if (!req.body.title || !req.body.body) {
res.status(400).send('Entries must have a title and body');
return;
}
entries.push({
title: req.body.title,
content: req.body.body,
published: new Date()
});
// 重定向到主页查看新的entry
res.redirect('/');
});
// 404 page
app.use(function(req, res) {
res.status(400).render('404');
});
http.createServer(app).listen('8888');
总结
本章主要是Express的具体操作和部分APIs的介绍。简单的谈了Express的2大特点:middleware, route。另外还使用到视图,视图如何通过模版引擎进行渲染。