淺析node中間件及實(shí)現(xiàn)一個(gè)簡(jiǎn)單的node中間件
一、是什么
中間件(Middleware)是介于應(yīng)用系統(tǒng)和系統(tǒng)軟件之間的一類(lèi)軟件,它使用系統(tǒng)軟件所提供的基礎(chǔ)服務(wù)(功能),銜接網(wǎng)絡(luò)上應(yīng)用系統(tǒng)的各個(gè)部分或不同的應(yīng)用,能夠達(dá)到資源共享、功能共享的目的
在NodeJS
中,中間件主要是指封裝http
請(qǐng)求細(xì)節(jié)處理的方法
例如在express
、koa
等web
框架中,中間件的本質(zhì)為一個(gè)回調(diào)函數(shù),參數(shù)包含請(qǐng)求對(duì)象、響應(yīng)對(duì)象和執(zhí)行下一個(gè)中間件的函數(shù)
在這些中間件函數(shù)中,我們可以執(zhí)行業(yè)務(wù)邏輯代碼,修改請(qǐng)求和響應(yīng)對(duì)象、返回響應(yīng)數(shù)據(jù)等操作
二、封裝
koa
是基于NodeJS
當(dāng)前比較流行的web
框架,本身支持的功能并不多,功能都可以通過(guò)中間件拓展實(shí)現(xiàn)。通過(guò)添加不同的中間件,實(shí)現(xiàn)不同的需求,從而構(gòu)建一個(gè) Koa
應(yīng)用
Koa
中間件采用的是洋蔥圈模型,每次執(zhí)行下一個(gè)中間件傳入兩個(gè)參數(shù):
- ctx :封裝了request 和 response 的變量
- next :進(jìn)入下一個(gè)要執(zhí)行的中間件的函數(shù)
下面就針對(duì)koa
進(jìn)行中間件的封裝:
Koa
的中間件就是函數(shù),可以是async
函數(shù),或是普通函數(shù)
// async 函數(shù) app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); // 普通函數(shù) app.use((ctx, next) => { const start = Date.now(); return next().then(() => { const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); });
下面則通過(guò)中間件封裝http
請(qǐng)求過(guò)程中幾個(gè)常用的功能:
token校驗(yàn)
module.exports = (options) => async (ctx, next) { try { // 獲取 token const token = ctx.header.authorization if (token) { try { // verify 函數(shù)驗(yàn)證 token,并獲取用戶相關(guān)信息 await verify(token) } catch (err) { console.log(err) } } // 進(jìn)入下一個(gè)中間件 await next() } catch (err) { console.log(err) } }
日志模塊
const fs = require('fs') module.exports = (options) => async (ctx, next) => { const startTime = Date.now() const requestTime = new Date() await next() const ms = Date.now() - startTime; let logout = `${ctx.request.ip} -- ${requestTime} -- ${ctx.method} -- ${ctx.url} -- ${ms}ms`; // 輸出日志文件 fs.appendFileSync('./log.txt', logout + '\n') }
Koa
存在很多第三方的中間件,如koa-bodyparser
、koa-static
等
下面再來(lái)看看它們的大體的簡(jiǎn)單實(shí)現(xiàn):
koa-bodyparser
koa-bodyparser
中間件是將我們的 post
請(qǐng)求和表單提交的查詢字符串轉(zhuǎn)換成對(duì)象,并掛在 ctx.request.body
上,方便我們?cè)谄渌虚g件或接口處取值
// 文件:my-koa-bodyparser.js const querystring = require("querystring"); module.exports = function bodyParser() { return async (ctx, next) => { await new Promise((resolve, reject) => { // 存儲(chǔ)數(shù)據(jù)的數(shù)組 let dataArr = []; // 接收數(shù)據(jù) ctx.req.on("data", data => dataArr.push(data)); // 整合數(shù)據(jù)并使用 Promise 成功 ctx.req.on("end", () => { // 獲取請(qǐng)求數(shù)據(jù)的類(lèi)型 json 或表單 let contentType = ctx.get("Content-Type"); // 獲取數(shù)據(jù) Buffer 格式 let data = Buffer.concat(dataArr).toString(); if (contentType === "application/x-www-form-urlencoded") { // 如果是表單提交,則將查詢字符串轉(zhuǎn)換成對(duì)象賦值給 ctx.request.body ctx.request.body = querystring.parse(data); } else if (contentType === "applaction/json") { // 如果是 json,則將字符串格式的對(duì)象轉(zhuǎn)換成對(duì)象賦值給 ctx.request.body ctx.request.body = JSON.parse(data); } // 執(zhí)行成功的回調(diào) resolve(); }); }); // 繼續(xù)向下執(zhí)行 await next(); }; };
koa-static
koa-static
中間件的作用是在服務(wù)器接到請(qǐng)求時(shí),幫我們處理靜態(tài)文件
const fs = require("fs"); const path = require("path"); const mime = require("mime"); const { promisify } = require("util"); // 將 stat 和 access 轉(zhuǎn)換成 Promise const stat = promisify(fs.stat); const access = promisify(fs.access) module.exports = function (dir) { return async (ctx, next) => { // 將訪問(wèn)的路由處理成絕對(duì)路徑,這里要使用 join 因?yàn)橛锌赡苁?/ let realPath = path.join(dir, ctx.path); try { // 獲取 stat 對(duì)象 let statObj = await stat(realPath); // 如果是文件,則設(shè)置文件類(lèi)型并直接響應(yīng)內(nèi)容,否則當(dāng)作文件夾尋找 index.html if (statObj.isFile()) { ctx.set("Content-Type", `${mime.getType()};charset=utf8`); ctx.body = fs.createReadStream(realPath); } else { let filename = path.join(realPath, "index.html"); // 如果不存在該文件則執(zhí)行 catch 中的 next 交給其他中間件處理 await access(filename); // 存在設(shè)置文件類(lèi)型并響應(yīng)內(nèi)容 ctx.set("Content-Type", "text/html;charset=utf8"); ctx.body = fs.createReadStream(filename); } } catch (e) { await next(); } } }
三、總結(jié)
在實(shí)現(xiàn)中間件時(shí)候,單個(gè)中間件應(yīng)該足夠簡(jiǎn)單,職責(zé)單一,中間件的代碼編寫(xiě)應(yīng)該高效,必要的時(shí)候通過(guò)緩存重復(fù)獲取數(shù)據(jù)
koa
本身比較簡(jiǎn)潔,但是通過(guò)中間件的機(jī)制能夠?qū)崿F(xiàn)各種所需要的功能,使得web
應(yīng)用具備良好的可拓展性和組合性
通過(guò)將公共邏輯的處理編寫(xiě)在中間件中,可以不用在每一個(gè)接口回調(diào)中做相同的代碼編寫(xiě),減少了冗雜代碼,過(guò)程就如裝飾者模式
到此這篇關(guān)于淺析node中間件及實(shí)現(xiàn)一個(gè)簡(jiǎn)單的node中間件的文章就介紹到這了,更多相關(guān)node中間件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Node.js利用Net模塊實(shí)現(xiàn)多人命令行聊天室的方法
Node.js Net 模塊提供了一些用于底層的網(wǎng)絡(luò)通信的小工具,包含了創(chuàng)建服務(wù)器/客戶端的方法,下面這篇文章主要給大家介紹了Node.js利用Net模塊實(shí)現(xiàn)命令行多人聊天室的方法,有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-12-12使用NodeJs 開(kāi)發(fā)微信公眾號(hào)(三)微信事件交互實(shí)例
這篇文章主要介紹了使用NodeJs 開(kāi)發(fā)微信公眾號(hào)(三)微信事件交互實(shí)例的相關(guān)資料,需要的朋友可以參考下2016-03-03Node.js同時(shí)安裝多個(gè)版本及相關(guān)配置指南(簡(jiǎn)單易操作)
在實(shí)際開(kāi)發(fā)過(guò)程中我們可能需要安裝多個(gè)版本的 nodejs,下面這篇文章主要給大家介紹了關(guān)于Node.js同時(shí)安裝多個(gè)版本及相關(guān)配置的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11koa2 數(shù)據(jù)api中間件設(shè)計(jì)模型的實(shí)現(xiàn)方法
這篇文章主要介紹了koa2 數(shù)據(jù)api中間件設(shè)計(jì)模型的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Node.js 文件夾目錄結(jié)構(gòu)創(chuàng)建實(shí)例代碼
下面小編就為大家?guī)?lái)一篇Node.js 文件夾目錄結(jié)構(gòu)創(chuàng)建實(shí)例代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07Nodejs環(huán)境Eggjs加簽驗(yàn)簽示例代碼
這篇文章主要介紹了Nodejs環(huán)境Eggjs加簽驗(yàn)簽示例代碼,文章開(kāi)頭給大家介紹了加簽和驗(yàn)簽的基本概念,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07利用nodeJs anywhere搭建本地服務(wù)器環(huán)境的方法
今天小編就為大家分享一篇利用nodeJs anywhere搭建本地服務(wù)器環(huán)境的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Node.js?全局變量無(wú)法掛載問(wèn)題解決分析
這篇文章主要為大家介紹了Node.js?全局變量無(wú)法掛載問(wèn)題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04