本文主要記錄筆者在使用開源Node.js web框架Koa.js過程當中遇到的一個小bug,爲修復此Bug查找Koa及其middleware源碼的過程,以及最終發起Pull Request並被採納的過程。javascript
事情的原由是這樣的,在我剛入職當前公司時,因爲團隊組件不久,開發人員還沒有配備期權,尤爲缺少服務端(Java
)開發人員;而剛好有一個對內視頻服務的需求比較緊急,因此本人雖然是一名(資深)前端工程師,依然主動承擔起了Server
端開發的責任。項目最終選擇FE
們最愛的Node.js
進行開發,web
框架則選擇了Koa.js
。html
Service
中有一個功能是爲生成的視頻提供下載功能。由於僅是內部人員下載,加上天天都要生成,因此決定直接在存儲在服務器,而後提供連接供用戶下載。前端
因而在服務端選擇中間件koa-static
,煎蛋設置一下緩存便可。主要代碼以下:vue
const Koa = require('koa');
const serve = require('koa-static');
const app = new Koa();
app.use(
serve(path.join(__dirname + '/dist'), {
extensions: ['mp4'],
maxage: 1000 * 60 * 60 * 24 * 100
})
);
複製代碼
網頁部分提供一個下載按鈕,採用a
標籤加download
,外面套button
的形式,代碼以下(vue):java
<button><a :href="video.outputPath" download>下載</a></button>
複製代碼
因而,功能完成,順利上線,運營小mm們效率提高,齊聲誇讚,完滿解決。node
本集完。git
若是生活是童話故事,那麼上面即是結局。惋惜,生活不是童話。github
大概在今年(2018)2月左右,突然你們反映,下載按鈕不能用了,點擊後,都是直接在新的Tab
頁打開連接。web
遇到bug後,第一反應是分析,能用 -> 不能用 的過程當中,發生了什麼。通過大體判斷,能夠得出結論是chrome
自動升級後,對download
的支持發生了變化。chrome
接下來,個人第一反應是,是否是download
屬性沒有用好呢。因而去搜了搜標準,而後嘗試給賦值,結果發現同樣是不行。
這個時候我突然想到,能夠去看看別的網站,是否有一樣的問題,以及怎麼作的。
找了很久以後,發現了一個網站,視頻還能夠下載,因而在chrome Develop Tools
的Network
面板下,苦苦尋找差別。終於發現,在Response Header
的Content-Type
中存在差別。個人請求狀況以下:
而能夠下載的視頻請求,內容則是:Content-Type: video/mpeg4
。因而我懷疑,是否是瀏覽器把本身可以識別的擴展名直接打開,不能識別的則進行保存操做。那麼接下來要作的事情就簡單了:修改咱們的響應頭。
對於npm
安裝的package,我的建議直接去npm官網搜索,通常都會提供源碼地址,文檔地址。
因而直接進入npm官網,搜索koa-static
,進入該package主頁,發現以下內容:
- setHeaders Function to set custom headers on response.
既然官方直接提供了功能,那麼事情好辦了,直接加上吧。
修改Server端代碼以下:
app.use(
serve(path.join(__dirname + '/dist'), {
extensions: ['mp4'],
maxage: 1000 * 60 * 60 * 24 * 100,
setHeaders: function (res) {
res.setHeader('Content-Type', 'video/mpeg4');
}
})
);
複製代碼
歡天洗地,打開瀏覽器刷新重試,結果呢,無效!
柴犬屁股一沉,發現事情並不簡單
文檔救不了咱們,只能去看源碼了。好在這些中間件通常都短小精悍而且邏輯嚴謹,讀一讀仍是頗有價值的。
對於node/js的項目,用到的package,直接打開項目目錄下的node_modules
找到對應目錄閱讀就能夠了,十分方便。PS:大多數package入口在目錄下的 index.js
文件。
打開node_modules/koa-static/index.js
後,發現koa-static
直接把傳入的options
原封不動傳遞給了koa-send
:
function serve (root, opts) {
......
done = await send(ctx, ctx.path, opts)
複製代碼
因而繼續,打開node_modules/koa-send/index.js
,仔細閱讀代碼,發現對options中的setHeaders
處理以下:
// 此處爲一個Assertion,若setHeaders不是函數,直接拋出錯誤
const setHeaders = opts.setHeaders
if (setHeaders && typeof setHeaders !== 'function') {
throw new TypeError('option setHeaders must be function')
}
......
// 若是是函數,則將其加入到reponse header
if (setHeaders) setHeaders(ctx.res, path, stats)
複製代碼
這裏關於Assertion能夠多說一句,斷言是編程中很使用的一種技巧,不論是開發、調試過程當中快速發現錯誤,仍是線上的防護性編程。在《代碼大全》等經典書籍中都有介紹,推薦你們閱讀相關章節。
這麼看沒問題啊,傳入的config應該都使用了啊。因而繼續往下讀,發現玄機:
ctx.type = type(path, encodingExt)
...
/** * File type. */
function type (file, ext) {
return ext !== '' ? extname(basename(file, ext)) : extname(file)
}
複製代碼
原來,在setHeader以後,源代碼又根據文件擴展名,修改了其content-type。爲了驗證本身的想法,我簡單修改這裏的代碼,進行嘗試:
if (!ctx.type) ctx.type = type(path, encodingExt)
複製代碼
重啓服務,刷新後,發現效果以下:
果真ok了。
既然折騰了這麼一大圈,解決了問題,因而我決定一不作二不休,直接給koa-send
開源項目Pull Request,若是被採納,還算是給開源屆作了Contribution。
過程很簡單,到項目主頁,fork項目。到本身主頁,把fork的項目checkout到本地,修改代碼,commit, push。
修改的代碼很簡單,可是注意,這些開源項目通常會有很重視測試,因此若是有UT,必定記得添加用例。個人代碼具體以下(提交內容不包含註釋):
// 刪除原來代碼:ctx.type = type(path, encodingExt)
if (!ctx.type) ctx.type = type(path, encodingExt)
// 添加Test Case
it('should set the Content-Type', function (done) {
const app = new Koa()
app.use(async (ctx) => {
await send(ctx, '/test/fixtures/user.json')
})
request(app.listen())
.get('/')
.expect('Content-Type', /application\/json/)
.end(done)
})
複製代碼
而後到仍是到本身fork的項目中,選擇第二個Tab:Pull requests
,而後點擊New pull request
按鈕,選擇本身想提交的分支便可。
發起請求後,項目維護者愉快的採納了,因而我也有了對Node.js
生態開源圈的第一次貢獻,內心仍是很高興的。
Pull Request的地址在這裏。
這件事情也給我帶來了必定的思考,整理後,結論以下:
pull request is welcomed
,因此有時間能夠多讀一些源碼,找機會多作一些貢獻。以上就是此次修復bug、貢獻源碼的全過程以及給我帶來的思考。只作了一點小小的工做,謝謝你們。