58 lines
1.3 KiB
Go
58 lines
1.3 KiB
Go
package httpx
|
|
|
|
import (
|
|
"io"
|
|
"io/fs"
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
// SPA 提供后台静态页:存在的文件原样返回;无扩展名的前端路由回退 index.html;
|
|
// 有扩展名但不存在返回 404;访问目录不列清单,同样回退 index.html。
|
|
func SPA(fsys fs.FS) http.Handler {
|
|
files := http.FileServer(http.FS(fsys))
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
cleaned := path.Clean("/" + r.URL.Path)
|
|
rel := strings.TrimPrefix(cleaned, "/")
|
|
if rel == "" {
|
|
rel = "."
|
|
}
|
|
fi, err := fs.Stat(fsys, rel)
|
|
if err == nil && !fi.IsDir() {
|
|
files.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
if path.Ext(cleaned) != "" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
serveIndex(w, r, fsys)
|
|
})
|
|
}
|
|
|
|
func serveIndex(w http.ResponseWriter, r *http.Request, fsys fs.FS) {
|
|
f, err := fsys.Open("index.html")
|
|
if err != nil {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
defer func() { _ = f.Close() }()
|
|
st, err := f.Stat()
|
|
if err != nil || st.IsDir() {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
w.WriteHeader(http.StatusOK)
|
|
if r.Method == http.MethodHead {
|
|
return
|
|
}
|
|
_, _ = io.Copy(w, f)
|
|
}
|