1.什么是中間件
中間件就是一個(gè)請(qǐng)求處理方法,用其把用戶(hù)從請(qǐng)求到響應(yīng)的整個(gè)過(guò)程分發(fā)到多個(gè)中間件去處理鲁纠,這樣做的目的是提高代碼的靈活性妖枚,動(dòng)態(tài)可擴(kuò)展的魄衅。簡(jiǎn)單的理解就是:將收到的請(qǐng)求進(jìn)行逐層過(guò)濾峭竣。
2.express中的中間件
請(qǐng)求處理的過(guò)程是:當(dāng)服務(wù)器接收到請(qǐng)求之后,如果服務(wù)器寫(xiě)了多個(gè)中間件晃虫,則按照順序依次匹配皆撩,直到匹配到符合要求的中間件,然后進(jìn)行處理哲银。需要注意的是:同一個(gè)請(qǐng)求所經(jīng)過(guò)的中間件都是同一個(gè)請(qǐng)求對(duì)象和響應(yīng)對(duì)象
中間件分類(lèi):
-
應(yīng)用程序級(jí)別中間件
-
全匹配(不關(guān)心任何請(qǐng)求路徑和請(qǐng)求方法毅访,當(dāng)用戶(hù)請(qǐng)求的時(shí)候如果分發(fā)到該中間件則直接進(jìn)行處理請(qǐng)求操作)
app.use(function (req,res,next) { console.log('全匹配'); next(); })
當(dāng)請(qǐng)求經(jīng)過(guò)這個(gè)中間件的時(shí)候,不關(guān)心請(qǐng)求路徑和方法盘榨,直接進(jìn)入該中間件進(jìn)行處理喻粹。其中
next
是一個(gè)方法,用于調(diào)用下一個(gè)符合條件的中間件草巡。如果不寫(xiě)next
守呜,則會(huì)在當(dāng)前中間件停留下來(lái),不會(huì)再去匹配其他中間件山憨。 -
路徑以
/xx/
開(kāi)頭的匹配(模糊匹配)app.use('/a',function (req,res,next) { console.log(3333); })
只有以
/a/
開(kāi)頭的路徑才可以匹配成功并處理查乒,比如:/a/b
是可以匹配成功的,但是/ab/b
不能匹配成功
-
-
路由級(jí)別中間件(精確匹配)
必須與請(qǐng)求路徑和請(qǐng)求方法一致才匹配成功
app.get('/a',(req,res) => { console.log(1111); res.send('index.html'); })
next使用:
- 沒(méi)有參數(shù)
查看以下場(chǎng)景:
app.use(function (req,res,next) {
console.log('全匹配');
next();
})
app.use('/a',function (req,res,next) {
console.log(3333);
next();
})
app.get('/a',(req,res) => {
console.log(1111);
res.render('index.html');
})
app.get('/',(req,res) => {
console.log('2222');
res.render('index.html',{
name: 'chen'
})
})
app.get('/',(req,res) => {
console.log('44444');
})
瀏覽器輸入url127.0.0.1:3000/
依次遍歷每個(gè)中間件郁竟,匹配到第一個(gè)完全匹配的中間件玛迄,打印出全匹配
之后,調(diào)用了next
棚亩,然后繼續(xù)去匹配符合要求的中間件蓖议,遇到第二個(gè)全匹配中間件不符合以/a/
開(kāi)頭的路徑,則直接掠過(guò)讥蟆,繼續(xù)匹配第三個(gè)勒虾,由于是精確匹配,仍然匹配不成功瘸彤,當(dāng)遇到最后一個(gè)中間件的時(shí)候修然,匹配成功,輸出2222
质况,可以看出next
查找的條件一直都是瀏覽器請(qǐng)求對(duì)象愕宋,也即需要符合127.0.0.1:3000/
這個(gè)請(qǐng)求。
再來(lái)另一個(gè)場(chǎng)景:
app.get('/a',(req,res,next) => {
console.log(1111);
next();
})
app.get('/a',(req,res) => {
res.send('ok');
})
當(dāng)訪問(wèn)/a
時(shí)结榄,匹配到第一個(gè)中間件輸出1111
中贝,遇到next
,匹配到第二個(gè)中間件潭陪,返回客戶(hù)端ok
雄妥,可以看出,請(qǐng)求匹配中間件是逐一匹配的過(guò)程依溯,而不是后者覆蓋前者的一個(gè)過(guò)程老厌。
-
帶有參數(shù)
app.use(function (req,res,next) { console.log('全匹配'); let err = '出錯(cuò)了'; next(err); }) app.use('/a',function (req,res,next) { console.log(3333); next(); }) # 帶有四個(gè)參數(shù),且這四個(gè)參數(shù)必須寫(xiě)全 app.use(function (err,req,res,next) { console.log(err);// 出錯(cuò)了 })
當(dāng)訪問(wèn)
/a
路徑的時(shí)候黎炉,匹配到第一個(gè)中間件枝秤,遇到next,并且?guī)в袇?shù)慷嗜,則不會(huì)再去匹配其他的中間件淀弹,直接去帶有四個(gè)參數(shù)的中間件去匹配,可以用于統(tǒng)一處理錯(cuò)誤響應(yīng)
使用第三方插件的原理:
由于:同一個(gè)請(qǐng)求所經(jīng)過(guò)的中間件都是同一個(gè)請(qǐng)求對(duì)象和響應(yīng)對(duì)象庆械,所以當(dāng)一個(gè)中間件在請(qǐng)求對(duì)象上添加其他內(nèi)容之后薇溃,其他被匹配成功的中間件可以訪問(wèn)到這個(gè)新增的內(nèi)容,例如:
# 訪問(wèn)'/'
app.use(function (req,res,next) {
console.log(111);
req.body = {
name: 'chen'
}
next()
})
app.use('/',function (req,res,next) {
console.log(req.body.name);// chen
})
以上代碼就展示了當(dāng)匹配到第一個(gè)中間件的時(shí)候缭乘,在請(qǐng)求對(duì)象上新增了body屬性沐序,然后調(diào)用第二個(gè)匹配的中間件的時(shí)候,可以訪問(wèn)到新增的屬性?xún)?nèi)容堕绩。而第三方插件也就是這種方式策幼,修改請(qǐng)求對(duì)象并返回相應(yīng)的內(nèi)容,當(dāng)訪問(wèn)其他中間件的時(shí)候奴紧,就可以訪問(wèn)到第三方插件新增的內(nèi)容特姐,比如:body-parser
插件
# 假設(shè)已經(jīng)下載好了body-parser插件
# 引入包
const bodyParser = require('body-parser');
# 將body-parser函數(shù)作為參數(shù)傳遞到app.use中
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.get('/',(req,res)=> {
console.log(req.body);
})
當(dāng)將bodyParser作為函數(shù)傳入中間件時(shí),此時(shí)會(huì)對(duì)請(qǐng)求對(duì)象進(jìn)行添加body屬性等操作黍氮,添加完成之后唐含,務(wù)必會(huì)調(diào)用next
,然后繼續(xù)匹配下一個(gè)中間件沫浆,此時(shí)在精確匹配中間件中就可以使用body屬性了觉壶。
從以上一系列的使用過(guò)程中,可以看出件缸,中間件書(shū)寫(xiě)順序還是影響請(qǐng)求響應(yīng)的铜靶。
3.總結(jié)
中間件作為請(qǐng)求的一種過(guò)濾手段,其中express中的中間件中主要依賴(lài)于next連接多個(gè)中間件他炊,同時(shí)由于:同一個(gè)請(qǐng)求所經(jīng)過(guò)的中間件都是同一個(gè)請(qǐng)求對(duì)象和響應(yīng)對(duì)象争剿,使得多個(gè)中間件之間可以進(jìn)行通信。
實(shí)際應(yīng)用:
- 采用中間件根據(jù)需求配置插件
- 對(duì)統(tǒng)一請(qǐng)求進(jìn)行過(guò)濾之后再處理請(qǐng)求
- 對(duì)于出錯(cuò)響應(yīng)可以進(jìn)行統(tǒng)一處理操作