Go語言中介軟體框架 Negroni 的靜態檔案處理原始碼分析

飛雪無情發表於2019-02-25

Negroni是一個非常棒的中介軟體,尤其是其中介軟體呼叫鏈優雅的設計,以及對GO HTTP 原生處理器的相容。我以前寫過兩篇文章,對Negroni進行了專門的分析,沒有看過的朋友可以在看下。

Go語言經典庫使用分析(五)| Negroni 中介軟體(一) www.flysnow.org/2017/08/20/…

Go語言經典庫使用分析(六)| Negroni 中介軟體(二) www.flysnow.org/2017/09/02/…

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等。

從示例程式碼中,可以看到,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或者網站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或者網站www.flysnow.org/,第一時間看後續精彩文章。覺得好的話,順手分享到朋友圈吧,感謝支援。

掃碼關注

相關文章