【Express】中介軟體 - AOP - 應用程式級別 - 路由級別 - 錯誤處理 - 內建 - 第三方

語言: CN / TW / HK

theme: fancy highlight: a11y-dark


小知識,大挑戰!本文正在參與“程式設計師必備小知識”創作活動。

本文已參與「掘力星計劃」,贏取創作大禮包,挑戰創作激勵金。

嗨!~ 大家好,我是YK菌 🐷 ,一個微系前端 ✨,喜歡分享自己學到的小知識 🏹,歡迎關注我呀 😘 ~ [微訊號: yk2012yk2012,微信公眾號:ykyk2012]

之前我們對Express進行了一個入門,今天來學習Express中介軟體相關內容,先介紹一些內建中介軟體的使用,後面我們做小demo的時候再用一些第三方中介軟體.

1. 示例

需求:輸出請求日誌

javascript app.get("/", (req, res) => { console.log(req.method, req.url, Date.now()); res.send("get /"); }); 如果要給每個路由都輸出請求日誌,我們可以封裝在一個函式中

javascript const myLooger = (req) => { console.log(req.method, req.url, Date.now()); }; app.get("/", (req, res) => { myLooger(req); res.send("get /"); }); 但是我們不想修改每一個路由,可以用中介軟體 【任何請求都會使用這個中介軟體】 ```javascript // req 請求物件 // res 響應物件 // next 下一個中介軟體 app.use((req, res, next) => { console.log(req.method, req.url, Date.now()) // 交出執行權,往後繼續匹配執行 next() })

app.get("/", (req, res) => { res.send("get /"); }); ```

中介軟體的順序很重要【從上往下】 【路由】也是一種【中介軟體】

2. 概念解析

2.1 中介軟體與AOP

Express的最大特色,也是最重要的一個設計,就是中介軟體。

在不修改原有程式碼的基礎上,增加一些新的功能

一個Express應用,就是由許許多多的中介軟體來完成的。

Express中介軟體類似於AOP面向切面程式設計 都是需要經過一些步驟,不用去修改自己的程式碼,從此來拓展或處理一些功能

  • AOP(Aspect Oriented Programming)面向切面程式設計
    • 將日誌記錄,效能統計、安全控制、事務處理、異常處理等程式碼從業務邏輯程式碼中劃分出來,通過對這些行為的分離,希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行為的時候不影響業務邏輯的程式碼
    • 利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性同時提高了開發的效率和可維護性

在這裡插入圖片描述 總結:在現有的程式碼程式中,在程式生命週期或者橫向流程中,加入/減去一個或多個功能,不影響原有功能。

2.2 Express中的中介軟體

在Express中,中介軟體就是一個可以訪問請求物件、響應物件和呼叫next方法的一個函式

在這裡插入圖片描述

在中介軟體函式中,可以執行以下任何任務 - 執行任何程式碼 - 修改 requestresponse 響應物件【同一個生命週期】 - 結束請求響應週期 - 呼叫下一個中介軟體

【注意】如果當前的中介軟體功能沒有結束請求-響應週期,則必須呼叫next()將控制全傳遞給下一個中介軟體功能。否則,該請求將被掛起

3. 詳細使用

在Express中應用程式可以使用以下型別的中介軟體 - 應用程式級別中介軟體 - 路由級別中介軟體 - 錯誤處理中介軟體 - 內建中介軟體 - 第三方中介軟體

3.1 應用程式級別中介軟體

不關心請求路徑

javascript app.use((req, res, next) => { console.log("Time", Date.now()); next(); });

限定請求路徑

javascript app.use('/user/:id',(req, res, next) => { console.log("Request Type", req.method); // GET next(); }); 限定請求方法+請求路徑【路由中介軟體】

javascript app.get('/user/:id',(req, res, next) => { res.send('Hello World') });

多個處理函式

javascript app.use( "/user/:id", (req, res, next) => { console.log("Request URL", req.originalUrl); next(); }, (req, res, next) => { console.log("Request Type", req.method); next(); } );

為同一個路徑定義多個處理中介軟體

```javascript app.get( "/user/:id", (req, res, next) => { console.log("ID", req.params.id); next(); }, (req, res, next) => { res.send("User Info"); next(); } );

app.get("/user/:id", (req, res, next) => { console.log("123"); // res.end(req.params.id); }); ```

要從路由器中介軟體堆疊中跳過其餘中介軟體功能,請呼叫next('route')將控制權傳遞給下一條路由

【注意】next('route')僅在使用app.METHOD()router.METHOD()函式載入的中間函式中有效

此示例顯示了一箇中間件子堆疊,該子堆疊處理對/user/:id路徑的GET請求 ```javascript app.get( "/user/:id", (req, res, next) => { if (req.params.id === "0") next("route"); else next(); }, (req, res, next) => { res.send("regular"); } );

app.get("/user/:id", (req, res, next) => { res.send("special"); }); ```

中介軟體也可以在【陣列】中宣告為可重用。

此示例顯示了一個帶有中介軟體子堆疊的陣列,該子堆疊處理對/user/:id路徑的GET請求

```javascript function logOriginalUrl(req, res, next) { console.log("Request URL", req.originalUrl); next(); } function logMethod(req, res, next) { console.log("Requset Type", req.method); next(); } const logStuff = [logOriginalUrl, logMethod];

app.get("/user/:id", logStuff, (req, res, next) => { res.send("User Info"); }); ```

3.2 路由級別中介軟體

建立路由器檔案

router.js ```javascript const express = require('express')

// 1. 建立路由例項 // 路由例項相當於一個 mini Express例項 const router = express.Router()

// 2. 配置路由 router.get('/user', (req, res) => { res.send('get /user') })

router.post('/user/:id', (req, res) => { res.send(post /user/${req.params.id}) })

// 3. 匯出路由例項 // export default router module.exports = router

// 4. 將路由整合到 Express例項中[掛載] app.jsjavascript const express = require("express"); const router = require('./router')

const app = express(); const port = 3000; // 預設3000

// 4. 掛載路由 app.use(router) app.use('/yk', router)

app.listen(port, () => { console.log(Server running at http://localhost:${port}/); }); ``` 在這裡插入圖片描述

3.3 錯誤處理中介軟體

錯誤處理【匹配到了出現錯誤呼叫next(err)】

定義錯誤處理中介軟體函式,使用四個引數

一般是在所有中介軟體之後掛載錯誤處理中介軟體 javascript app.use((err, req, res, next) => { console.error(err.stack) res.status(500).send('Something broke!') }) 錯誤處理中介軟體始終帶有四個引數,必須提供四個引數,即使不需要next也要指定它,否則,將會解釋成常規中介軟體,並且無法處理錯誤

如果將任何內容傳遞給該next()函式('route'除外),Express都會將當前請求視為錯誤,並且將跳過所有剩餘的非錯誤處理路由和中介軟體函式

```javascript app.get("/todos", async (req, res, next) => { try { const db = await getDb(); res.status(200).json(db.todos); } catch (err) { // 跳過所有剩餘的無錯誤處理路由和中間函式 next(err); } });

app.use((err, req, res, next) => { console.log("錯誤", err); res.status(500).json({ error: err.message, }); }); ```

處理404【路由匹配不到情況】

通常會在所有的路由之後配置處理 404 的內容 javascript app.use((req, res, next) => { res.status(404).send("404 Not Found."); });

3.4 內建中介軟體

  • express.json() 解析Content-Type為application/json格式的請求體
  • express.urlencoded() 解析Content-Type 為application/x-www-form-urlencoded格式的請求體
  • express.raw() 解析Content-Type為application/octet-stream格式的請求體
  • express.text() 解析Content-Type為text/plain格式的請求體
  • express.static() 託管靜態資原始檔

3.5 第三方中介軟體

早期的Express內建了很多中介軟體。後來Express在4.x之後移除了這些內建中介軟體,官方把這些功能中介軟體以包的形式提供出來。這樣做的目的是為了保持Express本身極簡靈活的特性,開發人員可以根據自己的需要去靈活的使用。

https://www.expressjs.com.cn/resources/middleware.html

在這裡插入圖片描述 在這裡插入圖片描述

參考影片: Express 教程(基礎+實戰+原理)

「其他文章」