?“你就坐在我身邊,好不好”
什么是Express
官方給出的概念:Express 是基于 Node.js 平臺,快速、開放、極簡的 Web 開發框架。
通俗的理解:Express 的作用和 Node.js 內置的 http 模塊類似,是專門用來創建 Web 服務器的。
Express的本質:就是一個npm上的第三方包,提供了快速創建Web服務器的便捷方法。?
為什么要有Express
?http 內置模塊用起來很復雜,開發效率低;Express 是基于內置的 http 模塊進一步封裝出來的,能夠極大的提高開發效率。
Express和http的關系
類似于瀏覽器中 Web API 和 jQuery 的關系。前者是基于后者進一步封裝出來的。
Express能做什么
對于前端程序員來說,最常見的兩種服務器,分別是:
- Web 網站服務器:專門對外提供 Web 網頁資源的服務器。
- API 接口服務器:專門對外提供 API 接口的服務器。
使用 Express,我們可以方便、快速的創建 Web 網站的服務器或 API 接口的服務器。
Express的基本使用
安裝
在項目所處的目錄中,運行如下的終端命令,即可將 express 安裝到項目中使用:
npm i express
?創建基本的Web服務器
//導入express
const express=require('express')
//創建web服務器
const app=express()
//調用app.listen(端口號,啟動成功后的回調函數),啟動服務器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
監聽GET請求
通過app.get()方法,可以監聽客戶端的GET請求,具體請求語法如下:
//參數1:客戶端請求的URL地址
//參數2:請求對應的處理函數
//req:請求對象(包含了與請求相關的屬性和方法)
//res:響應對象(包含了與響應相關的屬性與方法)app.get('請求URL',function(req,res){
/*處理函數*/})
監聽POST請求
通過 app.post() 方法,可以監聽客戶端的 POST 請求,具體的語法格式如下:
//參數1:客戶端請求的URL地址
//參數2:請求對應的處理函數
//req:請求對象(包含了與請求相關的屬性和方法)
//res:響應對象(包含了與響應相關的屬性與方法)app.post('請求URL',function(req,res){
/*處理函數*/})
把內容響應給客戶端
通過 res.send() 方法,可以把處理好的內容,發送給客戶端:
app.get('/user',(req,res)=>{
//向客戶端發送JSON對象
res.send({name:'Camellia',age:19,gender:'女'{)
{)app.post('/user',(req,res)=>{
//向客戶端發送文本內容
res.send('請求成功‘)
})
獲取 URL 中攜帶的查詢參數
通過 req.query 對象,可以訪問到客戶端通過查詢字符串的形式,發送到服務器的參數:
app.get('/',(req,res)=>{
//req.query默認是一個空對象
//客戶端使用?name=Camellia&age=19 這種查詢字符串形式,發送到服務器的參數,
//可以通過req.query對象訪問到,例如:
//req.query.name req.query.qge
console.log(req.query)
})
獲取 URL 中的動態參數
通過 req.params 對象,可以訪問到 URL 中,通過 : 匹配到的動態參數:
//URL地址中,可以通過 :參數名 的形式,匹配動態參數值
app.get('/user/:id',(req,res)=>{
//req.params 默認是一個空對象
//里面存放著通過 :動態匹配到的值
console.log(req.params)
})
托管靜態資源
express.static()
express 提供了一個非常好用的函數,叫做 express.static(),通過它,我們可以非常方便地創建一個靜態資源服務器, 例如,通過如下代碼就可以將 public 目錄下的圖片、CSS 文件、JavaScript 文件對外開放訪問了:
app.use(express.static('public'))
現在,你就可以訪問 public 目錄中的所有文件了:
- http://localhost:3000/images/bg.jpg
- http://localhost:3000/css/style.css
- http://localhost:3000/js/login.js
Express 在指定的靜態目錄中查找文件,并對外提供資源的訪問路徑。 因此,存放靜態文件的目錄名不會出現在 URL 中。
托管多個靜態資源目錄
如果要托管多個靜態資源目錄,請多次調用 express.static() 函數:
app.use(express.satic('public'))
app/use(express.static('files'))
訪問靜態資源文件時,express.static() 函數會根據目錄的添加順序查找所需的文件。?
掛載路徑前綴
如果希望在托管的靜態資源訪問路徑之前,掛載路徑前綴,則可以使用如下的方式:
app.use('/public',express.static('/public'))
現在,你就可以通過帶有 /public 前綴地址來訪問 public 目錄中的文件了:
- http://localhost:3000/public/images/kitten.jpg
- http://localhost:3000/public/css/style.css
- http://localhost:3000/public/js/app.js
nodemon
為什么要使用 nodemon
在編寫調試 Node.js 項目的時候,如果修改了項目的代碼,則需要頻繁的手動 close 掉,然后再重新啟動,非常繁瑣。 現在,我們可以使用 nodemo(https://www.npmjs.com/package/nodemon) 這個工具,它能夠監聽項目文件 的變動,當代碼被修改后,nodemon 會自動幫我們重啟項目,極大方便了開發和調試。
安裝 nodemon
在終端中,運行如下命令,即可將 nodemon 安裝為全局可用的工具:
npm i -g nodemon
使用 nodemon
當基于 Node.js 編寫了一個網站應用的時候,傳統的方式,是運行 node app.js 命令,來啟動項目。這樣做的壞處是: 代碼被修改之后,需要手動重啟項目。
現在,我們可以將 node 命令替換為 nodemon 命令,使用 nodemon app.js 來啟動項目。這樣做的好處是:代碼 被修改之后,會被 nodemon 監聽到,從而實現自動重啟項目的效果。
Express路由
在 Express 中,路由指的是客戶端的請求與服務器處理函數之間的映射關系。 Express 中的路由分 3 部分組成,分別是請求的類型、請求的 URL 地址、處理函數,格式如下:
app.METHOD(PATH,HANDLER)
Express 中的路由的例子
//陪陪GET請求,且請求URL為/
app.get('/',function(req,res){
res.send('hello Camellia')
})
//匹配POST請求,且請求URL為/
app.post('/',function (req,res){
res.send('Got a POSR request')
})
路由的匹配過程
每當一個請求到達服務器之后,需要先經過路由的匹配,只有匹配成功之后,才會調用對應的處理函數。
在匹配時,會按照路由的順序進行匹配,如果請求類型和請求的 URL 同時匹配成功,則 Express 會將這次請求,轉 交給對應的 function 函數進行處理。
?路由的使用
最簡單的用法
在 Express 中使用路由最簡單的方式,就是把路由掛載到 app 上,示例代碼如下:
const express=rquire('express')
//創建Web服務器,命名為app
const app=express()
//掛載路由
app.get('/',(req,res)=>{
res.send('hello camellia')})
app.post('/',(req,res)=>{
res.send('POST request')})
//啟動Web服務器
app/listen(80,()=>{console.log('server running at http://127.0.0.1')})
模塊化路由
為了方便對路由進行模塊化的管理,Express 不建議將路由直接掛載到 app 上,而是推薦將路由抽離為單獨的模塊。 將路由抽離為單獨模塊的步驟如下:
- ①創建路由模塊對應的 .js 文件
- ② 調用 express.Router() 函數創建路由對象
- ③ 向路由對象上掛載具體的路由
- ④ 使用 module.exports 向外共享路由對象
- ⑤ 使用 app.use() 函數注冊路由模塊
創建路由模塊
//導入express
var express =require('express')
//創建路由對象
var router =express.Router()
//掛載獲取用戶列表的路由
router.get('/user/list',function(req,res){
res.send('Get user list.')
//掛載添加用戶的路由
router.post('/user/add',function(req,res){
res.send('Add new user.')
//向外導出路由對象
module.exports=router
注冊路由模塊
//導入路由模塊
const userRouter=require('./router/user.js')
//使用app.use()注冊路由模塊
app/use(useRouter)
為路由模塊添加前綴
類似于托管靜態資源時,為靜態資源統一掛載訪問前綴一樣,路由模塊添加前綴的方式也非常簡單:
//導入路由模塊
const userRouter=require('./router/user.js')
//使用app.use()注冊路由模塊,并添加統一的訪問前綴/apiapp.use('/api',userRouter)
Express中間件
中間件(Middleware ),特指業務流程的中間處理環節。
Express 中間件的調用流程
當一個請求到達 Express 的服務器之后,可以連續調用多個中間件,從而對這次請求進行預處理。
Express 中間件的格式
?Express 的中間件,本質上就是一個 function 處理函數,Express 中間件的格式如下:
?
注意:中間件函數的形參列表中,必須包含 next 參數。而路由處理函數中只包含 req 和 res。?
next 函數的作用
?next 函數是實現多個中間件連續調用的關鍵,它表示把流轉關系轉交給下一個中間件或路由。
?
Express中間件的初步體驗
?定義中間件函數
可以通過如下的方式,定義一個最簡單的中間件函數:
const mw=function(req,res,next){
console.log('這是要最簡單的中間件函數')
//注意:在當前中間件中的業務處理完畢之后,必須調用next()函數
//表明把流轉關系轉交給下一個中間件或者路由
next()
}
全局生效的中間件
客戶端發起的任何請求,到達服務器之后,都會觸發的中間件,叫做全局生效的中間件。
通過調用 app.use(中間件函數),即可定義一個全局生效的中間件,示例代碼如下:
const mw=function(req,res,next){
console.log('這是要最簡單的中間件函數')
next()
}//全局生效的中間件
app.use(mw)
定義全局中間件的簡化形式
app.use(function(req,res,next){
console.log('這是一個最簡單的中間件函數')
next()
})
中間件的作用
多個中間件之間,共享同一份 req 和 res。基于這樣的特性,我們可以在上游的中間件中,統一為 req 或 res 對象添 加自定義的屬性或方法,供下游的中間件或路由進行使用。
定義多個全局中間件
?可以使用 app.use() 連續定義多個全局中間件。客戶端請求到達服務器之后,會按照中間件定義的先后順序依次進行 調用,示例代碼如下:
app.use(function(req,res,next){
console.log('這是一個第一個中間件函數')
next()
})
app.use(function(req,res,next){
console.log('這是一個第二個中間件函數')
next()
})
app.use(function(req,res,next){
console.log('這是一個第三個中間件函數')
next()
})
局部生效的中間件
不使用 app.use() 定義的中間件,叫做局部生效的中間件,示例代碼如下:
// 定義中間件函數 mw1
const mw1 = function(req, res, next) {console.log('這是中間件函數')next()
}// mw1 這個中間件只在"當前路由中生效",這種用法屬于"局部生效的中間件"
app.get('/', mw1, function(req, res) {res.send('Home page.')
})// mw1 這個中間件不會影響下面這個路由↓↓↓
app.get('/user', function(req, res) { res.send('User page.') })
定義多個局部中間件
?可以在路由中,通過如下兩種等價的方式,使用多個局部中間件:
// 以下兩種寫法是“完全等價”的,可根據自己的喜好,選擇任意一種方式進行使用
app.get('/', mw1, mw2, (req, res) => { res.send('Home page.') })
app.get('/', [mw1, mw2], (req, res) => { res.send('Home page.') })
了解中間件的5個使用注意事項
- ?① 一定要在路由之前注冊中間件
- ② 客戶端發送過來的請求,可以連續調用多個中間件進行處理
- ③ 執行完中間件的業務代碼之后,不要忘記調用 next() 函數
- ④ 為了防止代碼邏輯混亂,調用 next() 函數后不要再寫額外的代碼
- ⑤ 連續調用多個中間件時,多個中間件之間,共享 req 和 res 對象
中間件的分類
Express 官方把常見的中間件用法,分成了 5 大類,分別是:
① 應用級別的中間件
② 路由級別的中間件
③ 錯誤級別的中間件
④ Express 內置的中間件
⑤ 第三方的中間件
應用級別的中間件
通過 app.use() 或 app.get() 或 app.post() ,綁定到 app 實例上的中間件,叫做應用級別的中間件,代碼示例如下:
//應用級別的中間件(全局中間件)
app.use((req,res,next)=>{
next()
})//應用級別的中間件(局部中間件)
app.get('/',mw1,(req,res)=>{
res.send('Home page .')
})
路由級別的中間件
綁定到 express.Router() 實例上的中間件,叫做路由級別的中間件。它的用法和應用級別中間件沒有任何區別。只不 過,應用級別中間件是綁定到 app 實例上,路由級別中間件綁定到 router 實例上,代碼示例如下:
var app = express()
var router = express.Router()// 路由級別的中間件
router.use(function (req, res, next) {console.log('Time:', Date.now())next()
})app.use('/', router)
錯誤級別的中間件
錯誤級別中間件的作用:專門用來捕獲整個項目中發生的異常錯誤,從而防止項目異常崩潰的問題。 格式:錯誤級別中間件的 function 處理函數中,必須有 4 個形參,形參順序從前到后,分別是 (err, req, res, next)。
app.get('/', function(req, res) { // 1. 路由throw new Error('服務器內部發生了錯誤!') // 1.1 拋出一個自定義的錯誤res.send('Home Page.')
})app.use(function (err, req, res, next) { // 2. 錯誤級別的中間件console.log('發生了錯誤:' + err.message) // 2.1 在服務器打印錯誤消息res.send('Error!' + err.message) // 2.2 向客戶端響應錯誤相關的內容
})
Express內置的中間件
自 Express 4.16.0 版本開始,Express 內置了 3 個常用的中間件,極大的提高了 Express 項目的開發效率和體驗:
① express.static 快速托管靜態資源的內置中間件,例如: HTML 文件、圖片、CSS 樣式等(無兼容性)
② express.json 解析 JSON 格式的請求體數據(有兼容性,僅在 4.16.0+ 版本中可用)
③ express.urlencoded 解析 URL-encoded 格式的請求體數據(有兼容性,僅在 4.16.0+ 版本中可用)
// 配置解析 application/json 格式數據的內置中間件
app.use(express.json())// 配置解析 application/x-www-form-urlencoded 格式數據的內置中間件
app.use(express.urlencoded({ extended: false }))
第三方的中間件
非 Express 官方內置的,而是由第三方開發出來的中間件,叫做第三方中間件。在項目中,大家可以按需下載并配置 第三方中間件,從而提高項目的開發效率。
例如:在 express@4.16.0 之前的版本中,經常使用 body-parser 這個第三方中間件,來解析請求體數據。使用步驟如下:
① 運行 npm install body-parser 安裝中間件
② 使用 require 導入中間件
③ 調用 app.use() 注冊并使用中間件
使用Express寫接口
創建基本的服務器
// 導入 express 模塊
const express = require('express')
// 創建 express 的服務器實例
const app = express()// write your code here...// 調用 app.listen 方法,指定端口號并啟動web服務器
app.listen(80, function () {console.log('Express server running at http://127.0.0.1')
})
創建 API 路由模塊?
// apiRouter.js 【路由模塊】
const express = require('express')
const apiRouter = express.Router()// bind your router here...module.exports = apiRouter// app.js 【導入并注冊路由模塊】
const apiRouter = require('./apiRouter.js')
app.use('/api', apiRouter)
編寫 GET 接口
?
apiRouter.get('/get', (req, res) => {// 1. 獲取到客戶端通過查詢字符串,發送到服務器的數據const query = req.query// 2. 調用 res.send() 方法,把數據響應給客戶端res.send({res.send({status: 0, // 狀態,0 表示成功,1 表示失敗msg: 'GET請求成功!', // 狀態描述data: query // 需要響應給客戶端的具體數據})})
編寫 POST 接口
?
apiRouter.post('/post', (req, res) => {// 1. 獲取客戶端通過請求體,發送到服務器的 URL-encoded 數據const body = req.body// 2. 調用 res.send() 方法,把數據響應給客戶端res.send({res.send({status: 0, // 狀態,0 表示成功,1 表示失敗msg: 'POST請求成功!', // 狀態描述消息data: body // 需要響應給客戶端的具體數據})})