:文件名即路由,160個(gè)接口如何自動(dòng)注冊(cè)到Express)
KuGouMusicApi源碼解析一文件名即路由160個(gè)接口如何自動(dòng)注冊(cè)到Express【免費(fèi)下載鏈接】KuGouMusicApi酷狗音樂(lè) Node.js API service項(xiàng)目地址: https://gitcode.com/gh_mirrors/ku/KuGouMusicApi本文帶你深入解析 KuGouMusicApi —— 一個(gè)流行的酷狗音樂(lè) Node.js API 服務(wù)。它的核心設(shè)計(jì)堪稱教科書(shū)級(jí)接口文件丟進(jìn)目錄就自動(dòng)變成 HTTP 路由無(wú)需任何手動(dòng)注冊(cè)代碼。我們將逐行剖析 Express 動(dòng)態(tài)路由注冊(cè)的完整鏈路帶你吃透這套架構(gòu)。? 30 秒總覽項(xiàng)目如何跑起來(lái)KuGouMusicApi 的目錄結(jié)構(gòu)非??酥坪诵闹挥腥龎K目錄 / 文件職責(zé)app.js啟動(dòng)入口一行調(diào)用startService()server.jsExpress 應(yīng)用構(gòu)建 動(dòng)態(tài)路由注冊(cè)引擎module/每個(gè).js文件 一個(gè)酷狗音樂(lè)接口當(dāng)前已有 200 個(gè)util/加密、簽名、請(qǐng)求等公共工具由 util/index.js 統(tǒng)一導(dǎo)出整個(gè)啟動(dòng)鏈路短到驚人——app.js 的全部?jī)?nèi)容只是async function start() { require(./util/runtime).applyCliOverrides(); await require(./server).startService(); }真正的魔法全部藏在 server.js 里。 核心魔法一文件名即路由這是本項(xiàng)目最優(yōu)雅的設(shè)計(jì)約定。觀察 module/ 目錄下的文件user_detail.js → /user/detail song_url.js → /song/url comment_music.js → /comment/music search_suggest.js → /search/suggest文件名中的下劃線_就是路由中的斜杠/。規(guī)則實(shí)現(xiàn)在 server.js#L118-L119 的parseRoute函數(shù)中const parseRoute (fileName) specificRoute fileName in specificRoute ? specificRoute[fileName] : /${fileName.replace(/\.(js)$/i, ).replace(/_/g, /)};三行代碼完成三件事去掉.js后綴 → 下劃線換成斜杠 → 補(bǔ)上首斜杠。這意味著開(kāi)發(fā)者新增一個(gè)接口時(shí)路由路徑在創(chuàng)建文件名的那一刻就已經(jīng)確定零配置、零注冊(cè)。 一個(gè)前綴的私有模塊約定注意 module/ 里還有兩個(gè)例外_comment.js 和 _listen_together_common.js。它們以_開(kāi)頭不會(huì)被注冊(cè)為路由。這是因?yàn)槎鄠€(gè)接口需要共享邏輯比如歌曲評(píng)論、專輯評(píng)論、彈幕都走同一套評(píng)論簽名流程公共代碼就抽到_前綴文件中由具體接口require復(fù)用。過(guò)濾規(guī)則在 server.js#L126.filter((fileName) fileName.endsWith(.js) !fileName.startsWith(_))一行 filter同時(shí)解決了哪些文件對(duì)外暴露的問(wèn)題。這種命名即行為的約定比在文件內(nèi)部加配置開(kāi)關(guān)清爽得多。?? 核心魔法二動(dòng)態(tài)掃描160 個(gè)接口一鍵注冊(cè)路由約定定好了誰(shuí)來(lái)批量執(zhí)行答案是 server.js#L105-L139 的getModulesDefinitions函數(shù)。它的處理流程掃描目錄fs.promises.readdir讀取module/下所有文件倒序排列.reverse()保證加載順序與入口邏輯一致過(guò)濾只保留.js結(jié)尾且非_開(kāi)頭的文件加載模塊require(modulePath)執(zhí)行文件并拿到導(dǎo)出函數(shù)生成定義數(shù)組每個(gè)文件變成{ identifier, route, module }三元組拿到數(shù)組后server.js#L328-L344 用一個(gè)for循環(huán)把所有接口掛到 Express 上const moduleDefinitions moduleDefs || (await getModulesDefinitions(path.join(__dirname, module), {})); for (const moduleDef of moduleDefinitions) { app.use(moduleDef.route, async (req, res) { /* 統(tǒng)一處理器 */ }); }160 個(gè)接口沒(méi)有一行app.get(/user/detail, ...)式的硬編碼注冊(cè)。目錄里加一個(gè)文件服務(wù)重啟后接口自動(dòng)上線——這就是約定優(yōu)于配置的極致體現(xiàn)。 設(shè)計(jì)亮點(diǎn)getModulesDefinitions支持傳入specificRoute參數(shù)可為個(gè)別文件指定特殊路由如把a(bǔ)lbum_new.js映射到/album/create在統(tǒng)一的默認(rèn)規(guī)則之外保留了逃生出口。 請(qǐng)求處理管線模塊函數(shù)被調(diào)用前發(fā)生了什么每個(gè)接口文件如 module/album.js導(dǎo)出的都是一個(gè)形如(params, useAxios) ...的普通函數(shù)它只知道如何拼裝酷狗官方請(qǐng)求完全不懂 HTTP。HTTP 相關(guān)的臟活累活全部由 server.js 中按順序掛載的中間件完成順序中間件作用1CORS 跨域?qū)Ψ庆o態(tài)請(qǐng)求設(shè)置跨域響應(yīng)頭OPTIONS 預(yù)檢直接返回 204見(jiàn) server.js#L183-L1952Cookie 解析手寫解析Cookie頭為鍵值對(duì)象掛到req.cookies見(jiàn) server.js#L210-L2213平臺(tái)標(biāo)識(shí)注入自動(dòng)補(bǔ)齊KUGOU_API_GUID、KUGOU_API_MID等設(shè)備標(biāo)識(shí) Cookie客戶端沒(méi)帶就生成默認(rèn)值見(jiàn) server.js#L238-L2744請(qǐng)求體解析JSON / 表單 / 二進(jìn)制三種類型限制 16mb / 5mb / 100mb52 分鐘緩存用 apicache 緩存 200 響應(yīng)相同 URL 兩分鐘內(nèi)只請(qǐng)求一次酷狗服務(wù)器見(jiàn) server.js#L318其中平臺(tái)標(biāo)識(shí)注入是酷狗生態(tài)特有的關(guān)鍵一步酷狗接口需要mid、guid、dfid等設(shè)備參數(shù)服務(wù)端會(huì)在客戶端缺失時(shí)自動(dòng)生成并寫回 Cookie首次調(diào)用接口就能用無(wú)需客戶端預(yù)配置。 統(tǒng)一路由處理器參數(shù)如何流入模塊函數(shù)循環(huán)中注冊(cè)的處理器server.js#L343-L444是所有接口共用的總調(diào)度它做了一次精妙的參數(shù)歸一化合并參數(shù)query 參數(shù) body 參數(shù) Cookie Authorization頭全部匯成一個(gè)query對(duì)象調(diào)用模塊moduleDef.module(query, 請(qǐng)求工廠函數(shù))——第二個(gè)參數(shù)是個(gè)閉包內(nèi)部注入客戶端真實(shí) IP 后調(diào)用 util/request.js 的createRequest發(fā)起真正請(qǐng)求處理回寫 Cookie模塊返回的 cookie 數(shù)組通過(guò)Set-Cookie寫回客戶端統(tǒng)一異常兜底模塊拋出的錯(cuò)誤對(duì)象會(huì)被轉(zhuǎn)成帶status的響應(yīng)未識(shí)別錯(cuò)誤統(tǒng)一返回 404以 module/album.js 為例它拿到合并后的params就能安心干活module.exports (params, useAxios) { const userid params?.cookie?.userid || params?.userid || 0; // ...拼裝 dataMap 后... return useAxios({ baseURL: http://kmr.service.kugou.com, url: /v1/album, method: POST, data: dataMap, encryptType: android, cookie: params?.cookie || {}, }); };模塊作者完全不需要接觸req/res接口邏輯與 Web 框架徹底解耦。?? 實(shí)戰(zhàn)如何新增一個(gè)接口理解上述機(jī)制后擴(kuò)展流程就是三步全程不到一分鐘創(chuàng)建文件在 module/ 下新建demo_feature.js想要/demo/feature路由就命名為demo_feature.js編寫函數(shù)導(dǎo)出(params, useAxios) {...}用useAxios發(fā)起酷狗官方請(qǐng)求完事重啟服務(wù)或npm run dev熱重載接口立即生效不需要改 server.js不需要改 package.json不需要任何注冊(cè)表。如果新接口要復(fù)用評(píng)論簽名等公共邏輯把共享代碼放進(jìn)_前綴文件即可。 小結(jié)KuGouMusicApi 用一套極簡(jiǎn)的約定把添加接口的成本壓到了最低文件名即路由_變/前綴即私有命名瞬間完成注冊(cè)聲明目錄即注冊(cè)表getModulesDefinitions動(dòng)態(tài)掃描 for循環(huán)掛載160 接口零硬編碼關(guān)注點(diǎn)徹底分離模塊只管拼酷狗請(qǐng)求中間件管線統(tǒng)一處理 CORS、Cookie、緩存與容錯(cuò)這套約定優(yōu)于配置的動(dòng)態(tài)路由架構(gòu)對(duì)任何想用 Node.js 構(gòu)建聚合型 API 服務(wù)的項(xiàng)目都是值得抄的作業(yè)?!久赓M(fèi)下載鏈接】KuGouMusicApi酷狗音樂(lè) Node.js API service項(xiàng)目地址: https://gitcode.com/gh_mirrors/ku/KuGouMusicApi創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考