Go語言中間件框架 Negroni 的靜態文件處理源碼分析

Negroni是一個很是棒的中間件,尤爲是其中間件調用鏈優雅的設計,以及對GO HTTP 原生處理器的兼容。我之前寫過兩篇文章,對Negroni進行了專門的分析,沒有看過的朋友能夠在看下。html

Go語言經典庫使用分析(五)| Negroni 中間件(一) http://www.flysnow.org/2017/08/20/go-classic-libs-negroni-one.htmlgit

Go語言經典庫使用分析(六)| Negroni 中間件(二) http://www.flysnow.org/2017/09/02/go-classic-libs-negroni-two.htmlgithub

Negroni Static中間件

Negroni設計的時候,內置了3箇中間件,它們分別是Logger、Recovery和Static,用於日誌處理,故障恢復以及靜態文件處理,今天來分析下靜態文件處理的源碼實現。瀏覽器

Negroni的靜態文件處理中間件,主要是爲了把咱們電腦(服務器)上的文件託管到Web服務上,能夠經過HTTP的方式訪問查看對應的電腦上的文件。若是請求的文件不存在,那麼請求將會轉給下一個中間件;若是文件存在,那麼就會顯示靜態文件的內容,請求到此終止。服務器

使用Negroni實現靜態處理很是簡單,咱們看一個示例。函數

package main

import (
    "github.com/urfave/negroni"
    "net/http"
)

func main(){
    n := negroni.New()
    n.Use(negroni.NewLogger())
    n.Use(negroni.NewStatic(http.Dir("/tmp")))
    n.Run(":8080")
}

示例中,把/tmp目錄下的文件,託管在了Web服務器上,能夠經過HTTP的方式訪問到。假如/tmp下有一個1.txt文件,那麼咱們打開瀏覽器,輸入地址http://127.0.0.1:8080/1.txt便可訪問對應的1.txt文件的內容。網站

示例中,我添加了一個n.Use(negroni.NewLogger())中間件,用於打印訪問日誌,好比請求的URL等。spa

從示例代碼中,能夠看到,Negroni主要經過negroni.NewStatic函數,生成一個靜態文件處理的中間件。設計

// NewStatic returns a new instance of Static
func NewStatic(directory http.FileSystem) *Static {
    return &Static{
        Dir:       directory,
        Prefix:    "",
        IndexFile: "index.html",
    }
}

該函數接受一個http.FileSystem類型的參數,用於指定要處理的目錄。從函數的源代碼能夠看出,返回的實際上是一個*Static,這個Static結構體有三個字段。日誌

Negroni Static 結構體分析

type Static struct {
    // 靜態服務要處理目錄
    Dir http.FileSystem
    // 給這些靜態文件添加的URL前綴,主要用於對處理的靜態文件分類
    Prefix string
    // 索引文件,訪問Dir目錄的時候,顯示這個索引文件的內容
    IndexFile string
}

第一個字段咱們已經在示例中演示了,下面看看另外兩個字段的用法,我經過示例說明,更容易理解。

Negroni Static Prefix 分析

如今我要託管/tmp目錄下的文件,可是我又想經過http://hostname/tmp/1.txt這樣的方式進行歸類,讓訪問的人知道,這些文件是在tmp/下的,這就能夠用到Prefix字段了。咱們把原來的示例修改下看看。

func main(){
    n := negroni.New()
    n.Use(negroni.NewLogger())
    n.Use(&negroni.Static{
        Dir:       http.Dir("/tmp"),
        Prefix:    "/tmp",
        IndexFile: "index.html",
    })


    n.Run(":8080")
}

咱們再也不使用NewStatic方法,而是直接經過&negroni.Static{}構建一個靜態文件處理的中間件,在構建的時候,指定Prefix的值爲/tmp,如今咱們再訪問/tmp下的1.txt文件,就只能經過http://127.0.0.1:8080/tmp/1.txt,而不是原來的http://127.0.0.1:8080/1.txt,這樣咱們經過給URL加前綴,達到文件分類的目的,其實就是增長一個URL path。

本文爲原創文章,轉載註明出處&&,歡迎掃碼關注公衆號 flysnow_org或者網站 http://www.flysnow.org/,&&第一時間看後續精彩文章。以爲好的話,&&順手分享到朋友圈吧,感謝支持。

以上這些是如何實現的呢?咱們看下靜態文件處理的源代碼,結合分析。

file := r.URL.Path
    // if we have a prefix, filter requests by stripping the prefix
    if s.Prefix != "" {
        if !strings.HasPrefix(file, s.Prefix) {
            next(rw, r)
            return
        }
        file = file[len(s.Prefix):]
        if file != "" && file[0] != '/' {
            next(rw, r)
            return
        }
    }
    f, err := s.Dir.Open(file)

以上這段代碼,是經過URL路徑,查找對應的靜態文件的核心代碼。從源代碼中能夠看到,對Prefix不爲空的狀況進行了特殊處理,若是Prefix不爲空,那麼咱們就要從URL Path中去掉這個Prefix,由於Prefix是咱們本身強加入的,不屬於文件路徑中的部分,因此要剝離掉,這樣才能夠獲得要處理文件的真實路徑,也就是源代碼中的file變量,最後經過s.Dir.Open(file)打開文件,獲得其內容顯示便可。

Negroni Static IndexFile 分析

最後一個字段是IndexFile,用於指定索引文件。對於咱們使用過Apache、Nginx的都比較熟悉,咱們指定了index.html做爲索引文件後,那麼咱們再訪問http://127.0.0.1:8080/tmp/這個目錄,顯示的實際上是index.html文件的內容,由於咱們訪問的實際上是http://127.0.0.1:8080/tmp/index.html,這就是IndexFile的做用。

Negroni的靜態處理器中間件源代碼中,對於IndexFile也很是簡潔,容易理解。

// try to serve index file
    if fi.IsDir() {
        file = path.Join(file, s.IndexFile)
        f, err = s.Dir.Open(file)
    }
    //省略了無關代碼

這個源代碼處理很簡單,若是是一個目錄的話,就把目錄和IndexFile拼接成一個新的文件路徑,進行二次打開。

如何在請求頁面上顯示文件內容

在一系列的打開、判斷中,若是最後能夠找到正確的文件,拿到內容,那麼就能夠把內容展現到瀏覽器的頁面上了。

http.ServeContent(rw, r, file, fi.ModTime(), f)

很是簡潔的一段代碼,即達到了咱們的目的,該函數能夠把ReadSeeker中的內容,顯示到請求的頁面上。

func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
    sizeFunc := func() (int64, error) {
        size, err := content.Seek(0, io.SeekEnd)
        if err != nil {
            return 0, errSeeker
        }
        _, err = content.Seek(0, io.SeekStart)
        if err != nil {
            return 0, errSeeker
        }
        return size, nil
    }
    serveContent(w, req, name, modtime, sizeFunc, content)
}

相比io.Copyhttp.ServeContent能夠自動計算響應的內容長度、能夠自動識別內容的MIME類型,還能夠處理If-Match,If-Unmodified-Since,If-None-Match,If-Modified-Since和If-Range的要求。

由於* os.File實現了io.ReadSeeker接口,因此咱們能夠直接使用文件的內容。

小結

好了,到了這裏,咱們已經分析完了Negroni中靜態文件處理中間件的實現,看完後相信你也能夠寫本身的靜態文件處理服務了,能夠本身試試,寫一個和http.FileServer相似功能的靜態文件處理服務。

本文爲原創文章,轉載註明出處,歡迎掃碼關注公衆號 flysnow_org或者網站 http://www.flysnow.org/,第一時間看後續精彩文章。以爲好的話,順手分享到朋友圈吧,感謝支持。

掃碼關注

相關文章
相關標籤/搜索