文章首發於:github.com/USTB-musion…html
Express是一個簡潔、靈活的node.js Web應用開發框架,它提供一系列強大的特性,幫助你建立各類web和移動應用。豐富的HTTP快捷方法和任意排列組合的Connect中間件,讓你建立健壯、友好的API變得既快捷又簡單。node
本文將從如下幾部分進行總結:git
簡單地說,就是根據方法和路徑執行匹配成功後執行對應的回調函數。如下爲路由的使用例子:github
// express是一個函數
let express = require('./express-route')
// 監聽函數
let app = express()
// RESTFUL API根據方法名的不一樣 作對應的資源的處理
app.get('/name', (req, res) => {
res.end('name')
})
app.get('/age', (req, res) => {
res.end('9')
})
app.post('/name', (req, res) => {
res.end('post name')
})
// all匹配全部的方法, *表示匹配全部的路徑
app.all('*', function(req, res) {
res.end(req.method)
})
app.listen(3000, () => {
console.log('server start 3000')
})
複製代碼
經過觀察上面的例子能夠發現,能夠定義一個express-route.js文件,並暴露出express函數,get,post,all,listen等方法,將其引入便可運行上面的例子。如下爲簡易實現express路由的代碼:web
let http = require('http');
let url = require('url');
function createApplication() {
// 監聽函數
let app = (req, res) => {
// 取出每個layer
// 1. 獲取請求的方法
let getMethods = req.method.toLowerCase();
let { pathname } = url.parse(req.url, true);
for (let i = 0; i < app.routes.length; i++) {
let {method, path, handler} = app.routes[i];
if ((method === getMethods || method === 'all') && (path === pathname || path === '*')) {
// 匹配成功後執行對應的callback
handler(req, res);
}
}
res.end(`Cannot ${getMethods} ${pathname}`)
}
// 存放路由
app.routes = [];
// 實現all方法
app.all = function(path, handler) {
let layer = {
method: 'all', // 表示所有匹配
path,
handler
}
app.routes.push(layer);
}
// http.METHODS能夠獲取全部的http方法
http.METHODS.forEach(method => {
// 將方法名轉換成小寫的
method = method.toLocaleLowerCase();
// 實現get,post等方法
app[method] = function(path, handler) {
let layer = {
method,
path,
handler
}
app.routes.push(layer);
}
})
// 實現get方法
// app.get = function(path, handler) {
// let layer = {
// method: 'get',
// path,
// handler
// }
// app.routes.push(layer);
// }
// 實現listen方法
app.listen = function() {
let server = http.createServer(app)
server.listen(...arguments)
}
return app
}
module.exports = createApplication;
複製代碼
經過向外暴露一個createApplication函數,並返回app對象。經過http.METHODS能夠獲取全部的http方法,在app對象裏定義listen,all還有get,post等請求方法。當客戶端請求時即遍歷routes,當匹配到理由時,即執行相應的handler函數。express
如上圖所示,中間件就是處理HTTP請求的函數,用來完成各類特定的任務,好比檢查用戶是否登陸,檢查用戶是否有權限訪問,執行一個請求須要多長時間等。它的特色是:數組
// middleware use
// 中間件 在執行路由以前 要幹一些處理工做 就能夠採用中間件
let express = require('./express-middleware.js');
let app = express();
// use方法第一個參數缺省默認就是/
// 中間件能夠擴展一些方法
app.use('/name', function(req, res, next) {
res.setHeader('Content-Type','text/html;charset=utf-8');
console.log('middleware1');
next('名字不合法')
})
app.use('/', function(req, res, next) {
console.log('middleware2');
next();
})
app.get('/name', (req, res, next) => {
// res.setHeader('Content-Type','text/html;charset=utf-8');
res.end('姓名');
next('名字不合法')
})
app.get('/age', (req, res) => {
console.log(req.path);
console.log(req.hostname);
console.log(req.query);
res.end('年齡');
})
// 錯誤中間件(4個參數)放在路由的下面
app.use(function(err, req, res, next) {
console.log(err)
next()
})
app.listen(3001, () => {
console.log('server start 3001')
})
複製代碼
經過觀察上面的例子能夠發現,app.use方法調用中間件,其中next爲很是重要的一個參數。要運行上面的例子,能夠定義一個express-middware.js,來實現app.use方法,若是運行next,會調用下一個下一個中間件。其大體原理爲:定義一個next函數並定義一個index索引值,每調用一次next,索引值加1。若是當前的layer與routes中的項相等,則匹配成功。實現express中間件的簡易代碼以下:bash
let http = require('http');
let url = require('url');
function createApplication() {
// 監聽函數
let app = (req, res) => {
// 取出每個layer
// 獲取請求的方法
let getMethods = req.method.toLowerCase();
let { pathname } = url.parse(req.url, true);
// 經過next方法進行迭代
let index = 0;
function next(err) {
// 若是數組所有迭代完成尚未找到,說明路徑不存在
if (index === app.routes.length) {
return res.end(`Cannot ${getMethods} ${pathname}`)
}
// 每次調用next方法調用下一個layer
let { method, path, handler } = app.routes[index++];
if (err) {
// 若是有錯誤,應該去找錯誤中間件,錯誤中間件有一個特色,handler有4個參數
if (handler.length === 4) {
handler(err, req, res, next)
} else {
// 若是沒有匹配到,要將err繼續傳遞下去
next(err); // 繼續找下一個layer進行判斷
}
} else {
// 處理中間件
if (method === 'middleware') {
if (path === '/' || path === pathname || pathname.startsWith(path + '/')) {
handler(req, res, next);
} else {
// 若是這個中間件沒有匹配到,那麼繼續走下一個中間件匹配
next();
}
} else { // 處理路由
if ((method === getMethods || method === 'all') && (path === pathname || path === '*')) {
// 匹配成功後執行對應的callback
handler(req, res);
} else {
next();
}
}
}
}
// 中間件中的next方法
next();
res.end(`Cannot ${getMethods} ${pathname}`)
}
// 存放路由
app.routes = [];
// 實現use方法
app.use = function(path, handler) {
// use方法第一個參數能夠缺省,默認是/
if (typeof handler !== 'function') {
handler = path;
path = '/';
}
let layer = {
// 約定method是'middleware'就表示是一箇中間件
method: 'middleware',
path,
handler
}
// 將中間件放在容器內
app.routes.push(layer);
}
// express內置中間件
app.use(function(req, res, next) {
let { pathname, query } = url.parse(req.url, true);
let hostname = req.headers['host'].split(':')[0];
req.path = pathname;
req.query = query;
req.hostname = hostname;
next();
})
// 實現all方法
app.all = function(path, handler) {
let layer = {
method: 'all', // 表示所有匹配
path,
handler
}
app.routes.push(layer);
}
// http.METHODS能夠獲取全部的http方法
http.METHODS.forEach(method => {
// 將方法名轉換成小寫的
method = method.toLocaleLowerCase();
// 實現get,post等方法
app[method] = function(path, handler) {
let layer = {
method,
path,
handler
}
app.routes.push(layer);
}
})
// 實現listen方法
app.listen = function() {
let server = http.createServer(app)
server.listen(...arguments)
}
return app
}
module.exports = createApplication;
複製代碼
express經過中間件的形式擴展了不少的內置API,舉一個簡單的例子:app
app.use(function(req, res, next) {
let { pathname, query } = url.parse(req.url, true);
let hostname = req.headers['host'].split(':')[0];
req.path = pathname;
req.query = query;
req.hostname = hostname;
next();
})
複製代碼
便可在req封裝hostname, path, query, hostname。框架