express是一款簡(jiǎn)單的web開(kāi)發(fā)應(yīng)用框架,這里主要是一些主要api的使用方法和總結(jié)記錄。
中間件
首先,express的靈魂就是中間件,它能影響到使用者的開(kāi)發(fā)思路,能將請(qǐng)求響應(yīng)的流程拆分成不同模塊進(jìn)行開(kāi)發(fā),然后app.use調(diào)用(可以一個(gè)或多個(gè))。這就是其中間件的的基本特征,然后編寫(xiě)一個(gè)中間件也比較簡(jiǎn)單:
var express = require('express')
var app = express()
var myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}
var Login = function (req, res, next) {
// do something for login
next()
}
app.use(myLogger)
app.use(Login)
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000)
上面例子是摘自官方文檔的一小段代碼。首先app.use()就是調(diào)用中間件(myLogger,Login)的關(guān)鍵,然后(myLogger,Login)函數(shù)里有個(gè)next回調(diào)函數(shù),當(dāng)調(diào)用它時(shí)可以執(zhí)行下一個(gè)中間件。不過(guò)也可以不寫(xiě)(表示最后一個(gè)中間件)或者提前使用res.end()之類(lèi)的提前結(jié)束。
接下來(lái)是關(guān)于API的一些記錄,從express和其子實(shí)例,request,response,router五個(gè)主要API入手。
express
導(dǎo)入的express模塊可以直接執(zhí)行,然后生成子實(shí)例。但express本身也帶有一些方便的方法:
- express.json()
- express.raw()
- express.Router() // 陌生,這里不記錄
- express.static()
- express.text()
- express.urlencoded()
當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)時(shí),其參數(shù)有可能放在請(qǐng)求主體里,可以通過(guò)request的body、query、params的屬性來(lái)訪(fǎng)問(wèn)這些參數(shù)。但如果請(qǐng)求方法是post,且參數(shù)是通過(guò)JSON,XML或者就一段文本方式放在主體里傳過(guò)來(lái),直接拿req.body反而獲取不到,需要通過(guò)request的on監(jiān)聽(tīng)data事件,例如
app.post("/a", (req, res, next) => {
req.on("data", (chunk) => {
console.log(chunk.toString())
})
next()
})
然后express就提供了一些內(nèi)置的中間件,就例如express.json()、express.raw()、express.text(),甚至有解碼用的express.urlencoded(),只需在自己代碼之前用就可以了。
app.use(express.json())
app.use((req,res,next)=>{
// 自己代碼
next()
})
express.static()提供了一個(gè)比較方便的方法供請(qǐng)求靜態(tài)資源,他接受一個(gè)root路徑和一個(gè)對(duì)象參數(shù),當(dāng)訪(fǎng)問(wèn)者訪(fǎng)問(wèn)“/root/xxx.html”時(shí),express會(huì)幫我們自動(dòng)響應(yīng)響應(yīng)的“/root/xxx.html”文件,如果沒(méi)有就會(huì)調(diào)用next回調(diào)進(jìn)入下一中間件。
express子實(shí)例
感覺(jué)這個(gè)也可以當(dāng)做是express來(lái)記錄,但文檔也將其分開(kāi),那這里也分開(kāi)記錄(部分)。
var express = require('express')
var app = express() // the main app
var admin = express() // the main app
這里用app替代express子實(shí)例的叫法
app有兩個(gè)屬性locals、mountPath,一個(gè)mount事件,十九個(gè)方法。
app.locals作為局部變量可存儲(chǔ)對(duì)象,這個(gè)能一直在應(yīng)用程序的整個(gè)生命周期中保持不變,app存在局部變量也存在,人在塔在!
app.mountpath可以返回不同路徑模式的路徑數(shù)組,例如app和admin同時(shí)用get監(jiān)聽(tīng)“/”路徑,那么app.mountpath將可以有效區(qū)別request的路徑,這和request.baseurl的有點(diǎn)不同。
app.on('mount',callback(parent))也是在兩個(gè)子實(shí)例情況下應(yīng)用,當(dāng)mount事件安裝在父應(yīng)用程序上時(shí),該事件將在子應(yīng)用程序上觸發(fā)。父應(yīng)用程序?qū)鬟f給回調(diào)函數(shù)。
var admin = express()
var app= express()
admin.on('mount', function (parent) {
// 這里將被執(zhí)行
console.log('Admin Mounted')
console.log(parent) // refers to the parent app
})
admin.get('/', function (req, res) {
res.send('Admin Homepage')
})
app.use('/admin', admin)
app.get()有兩個(gè)用法,當(dāng)你傳一個(gè)字符串參數(shù)時(shí),它會(huì)返回上面所說(shuō)的局部變量locals的相應(yīng)值。當(dāng)兩個(gè)參數(shù)(一個(gè)字符串和一個(gè)回調(diào)函數(shù)),它就是監(jiān)聽(tīng)相應(yīng)路徑的get請(qǐng)求方法,這是這個(gè)方法基本和post,put,patch,delete,all,METHOD類(lèi)似的。
app.param([name], callback)是一個(gè)有趣的方法,param里如果存在相應(yīng)的[name]屬性,則執(zhí)行后面的callback。
app.set(name, value)比較特殊,可以存儲(chǔ)一些自定義的鍵值對(duì),但app也提供了一些特別的設(shè)置(例如:env,etag,jsonp callback name),可以前往官網(wǎng)查看。
app.use([path,] callback [, callback...])很常見(jiàn)也很重要,第一個(gè)參數(shù)是路徑,之后的就是回調(diào),當(dāng)匹配到相應(yīng)路徑執(zhí)行后面的回調(diào),當(dāng)然是按照先后順序,多個(gè)回調(diào)里的函數(shù)順序比下一個(gè)中間件執(zhí)行時(shí)機(jī)早。callback里有四個(gè)參數(shù),形式:
app.use(function ([err,] req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
如果使用四個(gè)參數(shù),那么它是一個(gè)錯(cuò)誤處理中間件,否則3個(gè)是常規(guī)中間件.
Request
request包含了app作為其屬性,所以通過(guò)req.app來(lái)訪(fǎng)問(wèn)app的所有權(quán)限。
request.baseUrl可以獲得app.use監(jiān)聽(tīng)的路徑。
request.body可以獲取請(qǐng)求存儲(chǔ)在主體的參數(shù),不過(guò)正常情況下都是undefined,所以才需要使用諸如express.json()或的正文解析中間件時(shí)進(jìn)行填充express.urlencoded() 。
request.cookies可以獲取請(qǐng)求里的cookie,如果沒(méi)有返回空對(duì)象。
request.hostname可以獲取域名,如果你要用得上的話(huà)。
request.query和request.params也可以獲取參數(shù),但query只需要在路由上寫(xiě)?a=1&b=2之類(lèi)的附加值就可以獲得,而params需要請(qǐng)求固定設(shè)置或路由上設(shè)置/user/:name之類(lèi)的。
request有一些方法,
req.accepts(types),根據(jù)請(qǐng)求的AcceptHTTP標(biāo)頭字段檢查指定的內(nèi)容類(lèi)型是否可接受,以下三個(gè)方法基本也是在設(shè)置headers的頭部字段
req.acceptsCharsets(charset [,...]),
req.acceptsEncodings(encoding [,...]),
req.acceptsLanguages(lang [, ...])
然后也可以通過(guò)req.get(type)來(lái)獲取某個(gè)字段的值,或者req.is(value)判斷該value是不是請(qǐng)求過(guò)來(lái)“Content-Type”的值。
Response
首先說(shuō)屬性,簡(jiǎn)稱(chēng)Response為res
res也可以訪(fǎng)問(wèn)app,也有個(gè)loacals屬性(但只能在本次請(qǐng)求周期使用)
res有21個(gè)方法,這里挑幾個(gè)常用的記錄:
append(field [, value])用于設(shè)置頭部的屬性和值;
cookie(name, value [, options])用于設(shè)置cookie;
clearCookie(name, [, options])用于清除cookie;
res.download(path [, filename] [, options] [, fn])用于響應(yīng)用戶(hù),下載指定的靜態(tài)文件等;
res.end([data] [, encoding])結(jié)束響應(yīng),和node的一致;
res.format(object)這個(gè)根據(jù)請(qǐng)求的accept字段值不同做不同的響應(yīng);
res.send([body])用于發(fā)送響應(yīng),但能發(fā)送很多類(lèi)型的數(shù)據(jù),除了文件
res.sendFile(path [, options] [, fn])和send相似,但傳送文件,注意path是絕對(duì)路徑
res.status(code)用于發(fā)送狀態(tài)碼,例如403等
res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly')
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true })
res.clearCookie('rememberme')
res.download('/report-12345.pdf')
res.format({
'text/plain': function () {
res.send('hey')
},
'text/html': function () {
res.send('<p>hey</p>')
},
'application/json': function () {
res.send({ message: 'hey' })
},
'default': function () {
// log the request and respond with 406
res.status(406).send('Not Acceptable')
}
})
Router
Router的用法基本和app.use監(jiān)聽(tīng)某些路由一樣,有五個(gè)方法:
- router.all()
- router.METHOD()
- router.param()
- router.route()
- router.use()
但從用法上感覺(jué)和app.xxx沒(méi)什么區(qū)別!