記一次對Koa.js middleware的源碼貢獻

本文主要記錄筆者在使用開源Node.js web框架Koa.js過程當中遇到的一個小bug,爲修復此Bug查找Koa及其middleware源碼的過程,以及最終發起Pull Request並被採納的過程。javascript

緣起

事情的原由是這樣的,在我剛入職當前公司時,因爲團隊組件不久,開發人員還沒有配備期權,尤爲缺少服務端(Java)開發人員;而剛好有一個對內視頻服務的需求比較緊急,因此本人雖然是一名(資深)前端工程師,依然主動承擔起了Server端開發的責任。項目最終選擇FE們最愛的Node.js進行開發,web框架則選擇了Koa.jshtml

問題

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 ToolsNetwork面板下,苦苦尋找差別。終於發現,在Response HeaderContent-Type中存在差別。個人請求狀況以下:

content-type-mp4.jpg

而能夠下載的視頻請求,內容則是: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)
複製代碼

重啓服務,刷新後,發現效果以下:

content-type-mpeg4.jpg

果真ok了。

Pull Request

既然折騰了這麼一大圈,解決了問題,因而我決定一不作二不休,直接給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按鈕,選擇本身想提交的分支便可。

pull-request.jpg

結論

發起請求後,項目維護者愉快的採納了,因而我也有了對Node.js生態開源圈的第一次貢獻,內心仍是很高興的。

Pull Request的地址在這裏

這件事情也給我帶來了必定的思考,整理後,結論以下:

  1. 寫代碼,解決問題,是充滿快樂的,可以給咱們帶來知足感。
  2. 認真調研,閱讀文檔,甚至深刻源碼,問題總歸是能夠解決的。
  3. 我發現這些開源項目其實都有issue,而且有些維護者也公開說了pull request is welcomed,因此有時間能夠多讀一些源碼,找機會多作一些貢獻。

以上就是此次修復bug、貢獻源碼的全過程以及給我帶來的思考。只作了一點小小的工做,謝謝你們。

相關文章
相關標籤/搜索