Express的官方描述是:一個基於Node.js平臺的快速,開放,極簡的web框架。這句話中有兩個關鍵詞:web
總而言之,Express其實就是在Node內置的HTTP模塊上構建了一層抽象,理論上全部Express可以實現的功能, 均可以經過Node來實現。相比於Node,Express具備的優點:數據庫
中間件(Middleware) 是一個函數,它能夠訪問請求對象(request), 響應對象(response), 和 web 應用中處於請求-響應循環流程中的 next的變量。接下來咱們先編寫一個簡單的express應用,看看究竟中間件是什麼?express
const express = require('express');
const app = express(); const fn = (req, res, next) => { console.log("hello,world"); }; app.use(fn) const port = 3000; app.listen(port,() => { console.log(`應用運行在${port}端口`); }) 複製代碼
當咱們訪問localhost:3000時就會發現,打印出了函數fn中的hello,world。這裏的函數fn就是一箇中間件,app.use(fn)就是運行這個中間件,實現他的功能。事實上,整個Express的功能就是由中間件組成,中間件具備的功能包括:編程
咱們經過代碼看一下中間件的這些功能:cookie
const express = require('express');
const app = express(); // 第一個中間件 app.use((req, res, next) => { console.log("中間件1"); next();// 若是沒有next則會被掛起 }) // 第二個中間件 app.use((req, res, next) => { console.log("中間件2"); //繼續執行第二個中間件 next(); }) // 第三個中間件 app.use((req, res, next) => { console.log("中間件3"); res.send('hello'); // 終結請求-響應循環 }) // 第四個中間件 app.use((req, res, next) => { console.log("中間件4"); // 中介後的中間件再也不執行 }); const port = 3000; app.listen(port,() => { console.log(`應用運行在${port}端口`); }) 複製代碼
最終的輸出結果爲:中間件1,中間件2,中間件3
。從上面的代碼中咱們能夠知道,若是沒有停止請求-響應循環,會執行全部的中間件;若是不是最後一個停止的中間件,必須執行next()。 咱們看一下express中間件的模型: 從上面的編程模型,咱們能夠看出每個中間件就是插入到請求開始和響應結束中間的東西,這也是中間件名字的由來。session
中間件的最大優勢就是模塊化,每個中間件是一個獨立的函數,實現一個特定的功能,而後經過app.sue將這個函數整合起來。抽離出來就是一個單獨的模塊。好比,咱們要實現一個功能,獲取當前請求的url,那麼咱們就能夠將這個功能,封裝成一箇中間件。app
//實現獲取url的模塊,封裝成中間件
const fn = (req, res, next) => { const url = req.url; console.log(url); next(); }; app.use(fn); const port = 3000; app.listen(port,() => { console.log(`應用運行在${port}端口`); }) 複製代碼
Express中的中間件,根據做用範圍大體能夠分爲應用級中間件和路由級中間件。事實上中間件的分類並無特別明顯的區別。框架
應用級中間件主要是經過app.use(),以及它的一些語法糖中應用的中間件。編輯器
const express = require('express');
const app = express(); // 應用級中間件 app.use((req, res, next) => { const url = req.url; next(); }); // 應用級中間件 app.get('/user',(req,res) => { res.send('應用級中間件'); }) const port = 3000; app.listen(port,() => { console.log(`應用運行在${port}端口`); }) 複製代碼
上面的代碼中,咱們調用了兩個中間件,一個是app.use()直接調用的中間件,另一個是app.use的語法糖app.get()執行的中間件,爲何說app.get()是app.use()的語法糖,由於app.get可以實現的方法使用app.use都可以實現。咱們試着將上面app.get的中間件用app.use實現。模塊化
app.use((req,res,next) => {
console.log(req.path) if(req.method === "GET" && req.path == '/users'){ res.send('使用app.use實現的中間件'); } }) // 功能上等價於 app.get('/user',(req,res) => { res.send('應用級中間件'); }) 複製代碼
const express = require('express');
const app = express(); // 定義一個路由 const user = express.Router(); user.use("/", (req, res) => { res.send("user.use"); }); user.get("/user", (req, res) => { res.send("路由級別中間件"); }); user.get("/blog", (req, res) => { res.send("路由級中間件"); }); const port = 3000; app.listen(port,() => { console.log(`應用運行在${port}端口`); }) 複製代碼
路由級中間件是經過使用express內置的Router生成一個小應用,這個應用跟app具備相同的功能,只不過它主要的功能是用來實現路由。 這種路由功能能夠很方便地幫助咱們進行路由模塊化開發。咱們能夠將相關的路由弄到同一個模塊當中。好比上面的user和blog路由能夠抽成 user子路由模塊和blog子路由模塊。示例以下:
const express = require('express');
const app = express(); const user = require('./routes/user.js'); const blog = require('./routes/blog.js'); // 抽離子路由 app.use('/user',user); app.use('/blog',blog); const port = 3000; app.listen(port,() => { console.log(`應用運行在${port}端口`); }) 複製代碼
以後只要路徑是/user的都會交給user這個子路由去處理。子路由user.js中代碼以下:
const express = require('express');
const router = express.Router(); router.get('/',(req,res) => { res.send('這裏是user路由'); }) module.exports = router; 複製代碼
在上面的講述中,咱們知道中間件是一個函數,它是嵌入到一個請求和響應循環中的一個獨立的功能塊。函數的參數是req,res,next。所以編寫一箇中間件就顯得很簡單了。至關於編寫一個函數,參數爲req,res,next。
// 定義
const getUrl = (req,res,next) => { console.log(req.url); next(); } // 使用 app.use(getUrl) 複製代碼
從上面的代碼咱們能夠看出,開發一箇中間件就是編寫一個函數。可是有時候爲了可以配置一些參數,咱們一般是返回一個函數,好比這樣:
const getUrl2 = (options) => {
return () => { console.log(`${options.pre}+req.url`); next(); } } app.use(getUrl2({pre:'url:'})) 複製代碼
這樣的話,就須要app.use(getUrl())這樣執行了。這就是爲何咱們常常看到app.use(bodyParser())這樣的使用方法了。實際上返回的仍是一個函數,只是爲了方便配置參數罷了。
最後咱們例舉一些express經常使用的中間件
到目前爲止,咱們詳細介紹了Express的核心中間件,從中間件的詳細理解到中間件的分類和使用,以及中間件的編寫,最後列舉了一些常見的中間件。經過本文咱們基本上就可以掌握Express的核心功能了,其餘的Express的API都是中間件的使用罷了。
本文使用 mdnice 排版