Golang HTTP FileServer 簡介
Posted on Jun 4, 2018 in Go 程式設計 - 初階 by Amo Chen ‐ 2 min read
Golang 內建提供 http.FileServer ,可以方便地透過 HTTP 存取檔案系統(file system),例如以下程式碼執行之後,就可以透過瀏覽器打開網址 http://localhost:8080
瀏覽 /usr/share/doc
資料夾內的檔案。
package main
import (
"net/http"
)
func main() {
http.ListenAndServe(":8080", http.FileServer(http.Dir("/usr/share/doc")))
}
也因為很方便,用 Golang 建構 web application 時,也可以利用 FileServer 幫忙處理靜態檔案(static files, 例如 CSS / JS / IMAGES)等,其範例如下:
package main
import (
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
上述範例先使用 http.FileServer
建立當前目錄下的 static 資料夾的 FileServer Handler ,然後再利用 http.Handle
將所有路徑請求是 /static/
開頭的 HTTP request 先利用 http.StripPrefix
將路徑的 /static/
字串去除後,轉交給 FileServer Handler 處理。
因此假設有一個 HTTP request 的路徑是 /static/index.js
的話,就會被換成 index.js
然後交給 FileServer Handler 處理,而 FileServer Handler 會試圖取得 ./static/index.js
的內容回應,如果檔案不存在的話,就會回應 404 NOT FOUND 。
這邊要注意的是 FileServer 是可以存取資料夾的,因此假設上述範例 HTTP request 的路徑是 /static/
的話,就會變成直接列出當前目錄下的 ./static
資料夾所有的檔案(如果你使用的是 http.Dir("/")
那就會整個作業系統的檔案被看光光,十分危險,千萬別這麼做,母湯母湯)。
要解決可以存取資料夾的問題的最簡單方法,就是在每個資料夾底下新增一個 index.html
即可。
As a special case, the returned file server redirects any request ending in “/index.html” to the same path, without the final “index.html”.
這種解決辦法適用於只有資料夾結構十分單純的情況下,如果資料夾內有多個資料夾,或者資料夾的結構很複雜,使用這種方式就相當辛苦,此時可以考慮將 FileServer 再包裝一層,解決此一困擾。以下是包裝過 FileServer 範例:
package main
import (
"log"
"net/http"
)
type securedFileSystem struct {
fs http.FileSystem
}
func (sfs securedFileSystem) Open(path string) (http.File, error) {
f, err := sfs.fs.Open(path)
if err != nil {
return nil, err
}
s, err := f.Stat()
if s.IsDir() {
index := strings.TrimSuffix(path, "/") + "/index.html"
if _, err := sfs.fs.Open(index); err != nil {
return nil, err
}
}
return f, nil
}
func main() {
fs := http.FileServer(securedFileSystem{http.Dir("./static")})
http.Handle("/static/", http.StripPrefix("/static/", fs))
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
上述範例最主要的關鍵部分在於實作 http.FileSystem 裡的 Open
,先讓 FileSytem 打開檔案,打開後判斷是否是資料夾,如果是資料夾,進一步看是否有 index.html
存在於檔案中,如果有的話就回傳 http.File ,沒有的話就回傳 err 。利用再包裝的方式達到阻擋存取資料夾的目的。
以上為本篇 http.FileServer
的介紹。
References
https://golang.org/pkg/net/http/#FileSystem
https://www.alexedwards.net/blog/disable-http-fileserver-directory-listings