滬江CCtalk視頻地址:https://www.cctalk.com/v/15114923887518
處理錯(cuò)誤請求愛能遮掩一切過錯(cuò)。
當(dāng)我們在訪問一個(gè)站點(diǎn)的時(shí)候,如果訪問的地址不存在(404),或服務(wù)器內(nèi)部發(fā)生了錯(cuò)誤(500),站點(diǎn)會(huì)展示出某個(gè)特定的頁面,比如:
那么如何在 Koa 中實(shí)現(xiàn)這種功能呢?其實(shí),一個(gè)簡單的中間件即可實(shí)現(xiàn),我們把它稱為 http-error。實(shí)現(xiàn)過程并不復(fù)雜,拆分為三步來看:
第一步:確認(rèn)需求
第二步:整理思路
第三步:代碼實(shí)現(xiàn)
打造一個(gè)事物前,需要先確認(rèn)它具有什么特性,這就是需求。
在這里,稍微整理下即可得到幾個(gè)基本需求:
在頁面請求出現(xiàn) 400 、 500 類錯(cuò)誤碼的時(shí)候,引導(dǎo)用戶至錯(cuò)誤頁面;
提供默認(rèn)錯(cuò)誤頁面;
允許使用者自定義錯(cuò)誤頁面。
現(xiàn)在,從一個(gè)請求進(jìn)入 Koa 開始說起:
一個(gè)請求訪問 Koa,出現(xiàn)了錯(cuò)誤;
該錯(cuò)誤會(huì)被 http-error 中間件捕捉到;
錯(cuò)誤會(huì)被中間件的錯(cuò)誤處理邏輯捕捉到,并進(jìn)行處理;
錯(cuò)誤處理邏輯根據(jù)錯(cuò)誤碼狀態(tài),調(diào)用渲染頁面邏輯;
渲染頁面邏輯渲染出對應(yīng)的錯(cuò)誤頁面。
可以看到,關(guān)鍵點(diǎn)就是捕捉錯(cuò)誤,以及實(shí)現(xiàn)錯(cuò)誤處理邏輯和渲染頁面邏輯。
基于教程目錄結(jié)構(gòu),我們創(chuàng)建 middleware/mi-http-error/index.js 文件,存放中間件的邏輯代碼。初始目錄結(jié)構(gòu)如下:
middleware/ ├─ mi-http-error/ │ └── index.js └─ index.js
注意: 目錄結(jié)構(gòu)不存在,需要自己創(chuàng)建。
該中間件第一項(xiàng)需要實(shí)現(xiàn)的功能是捕捉到所有的 http 錯(cuò)誤。根據(jù)中間件的洋蔥模型,需要做幾件事:
1. 引入中間件修改 middleware/index.js,引入 mi-http-error 中間件,并將它放到洋蔥模型的最外層
const path = require("path") const ip = require("ip") const bodyParser = require("koa-bodyparser") const nunjucks = require("koa-nunjucks-2") const staticFiles = require("koa-static") const miSend = require("./mi-send") const miLog = require("./mi-log") // 引入請求錯(cuò)誤中間件 const miHttpError = require("./mi-http-error") module.exports = (app) => { // 應(yīng)用請求錯(cuò)誤中間件 app.use(miHttpError()) app.use(miLog(app.env, { env: app.env, projectName: "koa2-tutorial", appLogLevel: "debug", dir: "logs", serverIp: ip.address() })); app.use(staticFiles(path.resolve(__dirname, "../public"))) app.use(nunjucks({ ext: "html", path: path.join(__dirname, "../views"), nunjucksConfig: { trimBlocks: true } })); app.use(bodyParser()) app.use(miSend()) }2. 捕獲中間件異常情況
修改 mi-http-error/index.js,在中間件內(nèi)部對內(nèi)層的其它中間件進(jìn)行錯(cuò)誤監(jiān)聽,并對捕獲 catch 到的錯(cuò)誤進(jìn)行處理
module.exports = () => { return async (ctx, next) => { try { await next(); /** * 如果沒有更改過 response 的 status,則 koa 默認(rèn)的 status 是 404 */ if (ctx.response.status === 404 && !ctx.response.body) ctx.throw(404); } catch (e) { /*此處進(jìn)行錯(cuò)誤處理,下面會(huì)講解具體實(shí)現(xiàn)*/ } } }
上面的準(zhǔn)備工作做完,下面實(shí)現(xiàn)兩個(gè)關(guān)鍵邏輯。
錯(cuò)誤處理邏輯其實(shí)很簡單,就是對錯(cuò)誤碼進(jìn)行判斷,并指定要渲染的文件名。這段代碼運(yùn)行在錯(cuò)誤 catch 中。
修改 mi-http-error/index.js:
module.exports = () => { let fileName = "other" return async (ctx, next) => { try { await next(); /** * 如果沒有更改過 response 的 status,則 koa 默認(rèn)的 status 是 404 */ if (ctx.response.status === 404 && !ctx.response.body) ctx.throw(404); } catch (e) { let status = parseInt(e.status) // 默認(rèn)錯(cuò)誤信息為 error 對象上攜帶的 message const message = e.message // 對 status 進(jìn)行處理,指定錯(cuò)誤頁面文件名 if(status >= 400){ switch(status){ case 400: case 404: case 500: fileName = status; break; // 其它錯(cuò)誤 指定渲染 other 文件 default: fileName = "other" } } } } }
也就是說,對于不同的情況,會(huì)展示不同的錯(cuò)誤頁面:
├─ 400.html ├─ 404.html ├─ 500.html ├─ other.html
這幾個(gè)頁面文件我們會(huì)在后面創(chuàng)建,接下來我們開始講述下頁面渲染的問題。
首先我們創(chuàng)建默認(rèn)的錯(cuò)誤頁面模板文件 mi-http-error/error.html,這里采用 nunjucks 語法。
Error - {{ status }} Error - {{ status }}
Looks like something broke!
{% if (env === "development") %}Message:
{{ error }}
Stack:
{% endif %}{{ stack }}
因?yàn)闋可娴轿募窂降慕馕?,我們需要引?path 模塊。另外,還需要引入 nunjucks 工具來解析模板。path 是 node 模塊,我們只需從 npm 上安裝nunjucks 即可。
安裝 nunjucks 模塊來解析模板文件:
npm i nunjucks -S
修改 mi-http-error/index.js,引入 path 和 nunjucks 模塊:
// 引入 path nunjucks 模塊 const Path = require("path") const nunjucks = require("nunjucks") module.exports = () => { // 此處代碼省略,與之前一樣 }
為了支持自定義錯(cuò)誤文件目錄,原來調(diào)用中間件的代碼需要修改一下。我們給中間件傳入一個(gè)配置對象,該對象中有一個(gè)字段 errorPageFolder,表示自定義錯(cuò)誤文件目錄。
修改 middleware/index.js:
// app.use(miHttpError()) app.use(miHttpError({ errorPageFolder: path.resolve(__dirname, "../errorPage") }))
注意: 代碼中,我們指定了 /errorPage 為默認(rèn)的模板文件目錄。
修改 mi-http-error/index.js,處理接收到的參數(shù):
const Path = require("path") const nunjucks = require("nunjucks") module.exports = (opts = {}) => { // 400.html 404.html other.html 的存放位置 const folder = opts.errorPageFolder // 指定默認(rèn)模板文件 const templatePath = Path.resolve(__dirname, "./error.html") let fileName = "other" return async (ctx, next) => { try { await next() if (ctx.response.status === 404 && !ctx.response.body) ctx.throw(404); } catch (e) { let status = parseInt(e.status) const message = e.message if(status >= 400){ switch(status){ case 400: case 404: case 500: fileName = status; break; default: fileName = "other" } }else{// 其它情況,統(tǒng)一返回為 500 status = 500 fileName = status } // 確定最終的 filePath 路徑 const filePath = folder ? Path.join(folder, `${fileName}.html`) : templatePath } } }
路徑和參數(shù)準(zhǔn)備好之后,我們需要做的事情就剩返回渲染的頁面了。
修改 mi-http-error/index.js,對捕捉到的不同錯(cuò)誤返回相應(yīng)的視圖頁面:
const Path = require("path") const nunjucks = require("nunjucks") module.exports = (opts = {}) => { // 增加環(huán)境變量,用來傳入到視圖中,方便調(diào)試 const env = opts.env || process.env.NODE_ENV || "development" const folder = opts.errorPageFolder const templatePath = Path.resolve(__dirname, "./error.html") let fileName = "other" return async (ctx, next) => { try { await next() if (ctx.response.status === 404 && !ctx.response.body) ctx.throw(404); } catch (e) { let status = parseInt(e.status) const message = e.message if(status >= 400){ switch(status){ case 400: case 404: case 500: fileName = status; break; default: fileName = "other" } }else{ status = 500 fileName = status } const filePath = folder ? Path.join(folder, `${fileName}.html`) : templatePath // 渲染對應(yīng)錯(cuò)誤類型的視圖,并傳入?yún)?shù)對象 try{ // 指定視圖目錄 nunjucks.configure( folder ? folder : __dirname ) const data = await nunjucks.render(filePath, { env: env, // 指定當(dāng)前環(huán)境參數(shù) status: e.status || e.message, // 如果錯(cuò)誤信息中沒有 status,就顯示為 message error: e.message, // 錯(cuò)誤信息 stack: e.stack // 錯(cuò)誤的堆棧信息 }) // 賦值給響應(yīng)體 ctx.status = status ctx.body = data }catch(e){ // 如果中間件存在錯(cuò)誤異常,直接拋出信息,由其他中間件處理 ctx.throw(500, `錯(cuò)誤頁渲染失敗:${e.message}`) } } } }
上面所做的是使用渲染引擎對模板文件進(jìn)行渲染,并將生成的內(nèi)容放到 Http 的 Response 中,展示在用戶面前。感興趣的同學(xué)可以去中間件源碼中查看 error.html 查看模板內(nèi)容(其實(shí)是從 koa-error 那里拿來稍作修改的)。
在代碼的最后,我們還有一個(gè)異常的拋出 ctx.throw(),也就是說,中間件處理時(shí)候也會(huì)存在異常,所以我們需要在最外層做一個(gè)錯(cuò)誤監(jiān)聽處理。
修改 middleware/index.js:
const path = require("path") const ip = require("ip") const bodyParser = require("koa-bodyparser") const nunjucks = require("koa-nunjucks-2") const staticFiles = require("koa-static") const miSend = require("./mi-send") const miLog = require("./mi-log") const miHttpError = require("./mi-http-error") module.exports = (app) => { app.use(miHttpError({ errorPageFolder: path.resolve(__dirname, "../errorPage") })) app.use(miLog(app.env, { env: app.env, projectName: "koa2-tutorial", appLogLevel: "debug", dir: "logs", serverIp: ip.address() })); app.use(staticFiles(path.resolve(__dirname, "../public"))) app.use(nunjucks({ ext: "html", path: path.join(__dirname, "../views"), nunjucksConfig: { trimBlocks: true } })); app.use(bodyParser()) app.use(miSend()) // 增加錯(cuò)誤的監(jiān)聽處理 app.on("error", (err, ctx) => { if (ctx && !ctx.headerSent && ctx.status < 500) { ctx.status = 500 } if (ctx && ctx.log && ctx.log.error) { if (!ctx.state.logged) { ctx.log.error(err.stack) } } }) }
下面,我們增加對應(yīng)的錯(cuò)誤渲染頁面:
創(chuàng)建 errorPage/400.html:
400 Error - {{ status }}
錯(cuò)誤碼 400 的描述信息
{% if (env === "development") %}Message:
{{ error }}
Stack:
{% endif %}{{ stack }}
創(chuàng)建 errorPage/404.html:
404 Error - {{ status }}
錯(cuò)誤碼 404 的描述信息
{% if (env === "development") %}Message:
{{ error }}
Stack:
{% endif %}{{ stack }}
創(chuàng)建 errorPage/500.html:
500 Error - {{ status }}
錯(cuò)誤碼 500 的描述信息
{% if (env === "development") %}Message:
{{ error }}
Stack:
{% endif %}{{ stack }}
創(chuàng)建 errorPage/other.html:
未知異常 Error - {{ status }}
未知異常
{% if (env === "development") %}Message:
{{ error }}
Stack:
{% endif %}{{ stack }}
errorPage 中的頁面展示內(nèi)容,可以根據(jù)自己的項(xiàng)目信息修改,以上僅供參考。
至此,我們基本完成了用來處理『請求錯(cuò)誤』的中間件。而這個(gè)中間件并不是固定的形態(tài),大家在真實(shí)項(xiàng)目中,還需要多考慮自己的業(yè)務(wù)場景和需求,打造出適合自己項(xiàng)目的中間件。
下一節(jié)中,我們將學(xué)習(xí)下規(guī)范與部署——制定合適的團(tuán)隊(duì)規(guī)范,提升開發(fā)效率。
上一篇:iKcamp新課程推出啦~~~~~iKcamp|基于Koa2搭建Node.js實(shí)戰(zhàn)(含視頻)? 處理靜態(tài)資源推薦: 翻譯項(xiàng)目Master的自述: 1. 干貨|人人都是翻譯項(xiàng)目的Master 2. iKcamp出品微信小程序教學(xué)共5章16小節(jié)匯總(含視頻)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107179.html
摘要:玩轉(zhuǎn)同時(shí)全面掌握潮流技術(shù)采用新一代的開發(fā)框架更小更富有表現(xiàn)力更健壯。融合多種常見的需求場景網(wǎng)絡(luò)請求解析模板引擎靜態(tài)資源日志記錄錯(cuò)誤請求處理。結(jié)合語句中轉(zhuǎn)中間件控制權(quán),解決回調(diào)地獄問題。注意分支中的目錄為當(dāng)節(jié)課程后的完整代碼。 ?? ?與眾不同的學(xué)習(xí)方式,為你打開新的編程視角 獨(dú)特的『同步學(xué)習(xí)』方式 文案講解+視頻演示,文字可激發(fā)深層的思考、視頻可還原實(shí)戰(zhàn)操作過程。 云集一線大廠...
摘要:云集一線大廠有真正實(shí)力的程序員團(tuán)隊(duì)云集一線大廠經(jīng)驗(yàn)豐厚的碼農(nóng),開源奉獻(xiàn)各教程。融合多種常見的需求場景網(wǎng)絡(luò)請求解析模板引擎靜態(tài)資源日志記錄錯(cuò)誤請求處理。結(jié)合語句中轉(zhuǎn)中間件控制權(quán),解決回調(diào)地獄問題。注意分支中的目錄為當(dāng)節(jié)課程后的完整代碼。 ?? ?與眾不同的學(xué)習(xí)方式,為你打開新的編程視角 獨(dú)特的『同步學(xué)習(xí)』方式 文案講解+視頻演示,文字可激發(fā)深層的思考、視頻可還原實(shí)戰(zhàn)操作過程。 云...
視頻地址:https://www.cctalk.com/v/15114923889408 showImg(https://segmentfault.com/img/remote/1460000012682164?w=1604&h=964); 文章 在前面幾節(jié)中,我們已經(jīng)實(shí)現(xiàn)了項(xiàng)目中的幾個(gè)常見操作:啟動(dòng)服務(wù)器、路由中間件、Get 和 Post 形式的請求處理等?,F(xiàn)在你已經(jīng)邁出了走向成功的第一步。 ...
POST/GET請求——常見請求方式處理 ?? iKcamp 制作團(tuán)隊(duì) 原創(chuàng)作者:大哼、阿干、三三、小虎、胖子、小哈、DDU、可木、晃晃 文案校對:李益、大力萌、Au、DDU、小溪里、小哈 風(fēng)采主播:可木、阿干、Au、DDU、小哈 視頻剪輯:小溪里 主站運(yùn)營:給力xi、xty 教程主編:張利濤 視頻地址:https://www.cctalk.com/v/15114357765870 ...
視頻地址:https://www.cctalk.com/v/15114923886141 showImg(https://segmentfault.com/img/remote/1460000012840997?w=1604&h=964); JSON 數(shù)據(jù) 我顛倒了整個(gè)世界,只為擺正你的倒影。 前面的文章中,我們已經(jīng)完成了項(xiàng)目中常見的問題,比如 路由請求、結(jié)構(gòu)分層、視圖渲染、靜態(tài)資源等。 那么,J...
閱讀 808·2021-10-14 09:43
閱讀 2133·2021-09-30 09:48
閱讀 3456·2021-09-08 09:45
閱讀 1102·2021-09-02 15:41
閱讀 1898·2021-08-26 14:15
閱讀 786·2021-08-03 14:04
閱讀 2985·2019-08-30 15:56
閱讀 3080·2019-08-30 15:52