Express是建立在NODEJS http模塊基礎上的網(wǎng)絡服務框架,具有以下幾個特點:
- 使用JavaScript作為開發(fā)語言,實現(xiàn)前端后端語言的一致性
- 最大特點:中間件 和 路由
- 靈活性,由于采用模塊化的理念,可以根據(jù)需求對服務進行組裝
express的基本概念,可以參見express官網(wǎng), 其核心內容很少。下面是自己學習的一些筆記。
一.第三方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中間件, 并規(guī)定輸出格式
// 還可以設置為其它選項,比如 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中安裝。這個中間件用于發(fā)送靜態(tài)資源,比如images, CSS 或HTML文件等。
假如應用的靜態(tài)資源存放在 public 目錄下,則可以通過此中間件發(fā)送文件:
var path = require('path');
// 使用NODE的 path模塊設置 public 路徑
var publicPath = path.resolve(__dirname, 'public');
// 聲明 'public' 目錄為靜態(tài)文件夾
app.use(express.static(publicPath));
app.use(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('沒有找到該靜態(tài)文件');
});
// ...
如果沒有找到文件,則app將進入下一個中間件,如果找到了,express.static將發(fā)送該文件,并且停止中間件的鏈式操作。
值得之一的是,開發(fā)過程中,可以使用 res.sendFile() 提供靜態(tài)文件,但是在生產環(huán)境中不要執(zhí)行此函數(shù),因為它必須針對每次文件請求讀取文件系統(tǒng),會產生嚴重的延遲,影響應用程序的總體性能。 建議使用 serve-static 中間件,這個中間件經過優(yōu)化,用于為Express應用程序提供文件。 還有一個更好的選項,就是使用逆向代理提供靜態(tài)文件。
其它
暫時先接觸到這些中間件,其它的一些中間件僅作列舉,以后碰到再詳談:
-
body-parser: 解析HTTP request body -
connect-ratelimit: 限制每小時連接數(shù)量,如果某人向服務器發(fā)送大量請求,這利用這個中間件返回錯誤,阻止服務器宕機 -
helmet: 給應用添加 HTTP headers, 用于阻止特定類型的攻擊 -
cookie-parser: 解析瀏覽器cookies -
response-time: 發(fā)送 X-Response-Time header,用于調試app性能
二.Routing
路由根據(jù) URL 和特定的 HTTP method 將請求(requests)匹配到特定的處理函數(shù)(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('這是主頁');
});
// 動態(tài)的路徑匹配 '/about/:name'
app.get('/about/:name', function(req, res) {
res.end(`${req.params.name} 的About頁面`)
});
// 如果沒有匹配到,則返回404
// 使用中間件
app.use(function(req, res) {
// 將狀態(tài)碼設置為 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對象添加了重定向方法,狀態(tài)碼默認為302, 有以下幾種形式:
res.redirect('/foo/bar')
res.redirect('http://example.com')
res.redirect(301, 'http://example.com')
res.redirect('..')
res.redirect('back')
# 1.完全匹配URL重定向到另一個網(wǎng)址
res.redirect('http://google.com');
# 2.相對host根目錄,比如現(xiàn)在在 ' http://example.com/admin/post/new'
// 下面可以重定向到 'http://example.com/admin'
res.redirect('/admin')
# 3.可以相對當前目錄,比如現(xiàn)在在 '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.相對路徑也是可能的,比如現(xiàn)在在 '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版本以上支持此方法。
發(fā)送指定path的文件, path 必須是一個絕對路徑。 Content-Type 根據(jù)文件的擴展設置。
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,并且發(fā)送已渲染的html字符串到客戶端,參數(shù):
locals: 一個對象,其屬性定義view中的本地變量
callback: 返回可能的錯誤和渲染的字符串,當發(fā)生錯誤,方法內部調用next(err)
view: 文件路徑字符串。可以是絕對路徑或相對 views 設置的相對路徑。如果路徑不包含文件擴展,則根據(jù)
view engine設置來決定文件擴展
local variable cache 使view緩存。設置為true,則開發(fā)時將緩存。對產品階段,view caching默認為true
// 發(fā)送渲染的view到客戶端
res.render('index')
// 如果回調指定,則渲染的html字符串必須顯式的發(fā)送
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字段(不區(qū)分大小寫)
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')
五.示例
下面是留言本的一個小網(wǎng)頁:
// 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。另外還使用到視圖,視圖如何通過模版引擎進行渲染。