我有点困惑.大多数例子都显示了两者的用法:http.ServeFile(..)
和http.FileServer(..)
,但似乎它们具有非常接近的功能.此外,我没有找到有关如何设置自定义NotFound处理程序的信息.
// This works and strip "/static/" fragment from path
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
// This works too, but "/static2/" fragment remains and need to be striped manually
http.HandleFunc("/static2/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path[1:])
})
http.ListenAndServe(":8080", nil)
Run Code Online (Sandbox Code Playgroud)
我试过阅读源代码,他们都使用serveFile(ResponseWriter, *Request, FileSystem, string, bool)
底层函数.但是使用自己的方法http.FileServer
返回并在提供文件之前进行一些准备工作(例如path.Clean()).fileHandler
ServeHTTP()
那么为什么需要这种分离?哪种方法更好用?我如何设置自定义NotFound处理程序,例如找不到请求的文件?
Cra*_*ast 65
主要区别在于,http.FileServer
有效地将HTTP前缀与文件系统进行了几乎1:1的映射.简单来说,它提供了整个目录路径.和它的所有孩子.
假设您有一个名为的目录,/home/bob/static
并且您已进行此设置:
fs := http.FileServer(http.Dir("/home/bob/static"))
http.Handle("/static/", http.StripPrefix("/static", fs))
Run Code Online (Sandbox Code Playgroud)
您的服务器将接受请求,例如/static/foo/bar
并提供服务/home/bob/static/foo/bar
(或404)
相比之下,它ServeFile
是一个较低级别的帮助程序,可用于实现类似于FileServer的操作,或实现您自己的路径可能,以及任何数量的东西.它只需要获取指定的本地文件并通过HTTP连接发送它.它本身不会提供整个目录前缀(除非你编写了一个类似于FileServer的查找处理程序)
注意天真地提供文件系统是一个潜在危险的事情(有可能突破root树的方法)因此我建议除非你真的知道你在做什么,否则使用http.FileServer
,http.Dir
因为它们包括检查以确保人们可以'突破FS,ServeFile
但没有.
附录
您的第二个问题,遗憾的是,如何自定义NotFound处理程序并不容易回答.因为serveFile
你注意到这是从内部函数调用的,所以没有超级容易的地方可以闯入.可能会有一些偷偷摸摸的事情,例如拦截你自己的响应ResponseWriter
,拦截404响应代码,但我会把这个练习留给你.
小智 6
这里有一个处理程序,如果找不到文件,它将重定向到“/”。在为 Angular 应用程序添加回退时,这会派上用场,正如此处所建议的,这是从 golang 服务中提供的。
注意:此代码未准备好用于生产。只是说明性的(充其量:-)
package main
import "net/http"
type (
// FallbackResponseWriter wraps an http.Requesthandler and surpresses
// a 404 status code. In such case a given local file will be served.
FallbackResponseWriter struct {
WrappedResponseWriter http.ResponseWriter
FileNotFound bool
}
)
// Header returns the header of the wrapped response writer
func (frw *FallbackResponseWriter) Header() http.Header {
return frw.WrappedResponseWriter.Header()
}
// Write sends bytes to wrapped response writer, in case of FileNotFound
// It surpresses further writes (concealing the fact though)
func (frw *FallbackResponseWriter) Write(b []byte) (int, error) {
if frw.FileNotFound {
return len(b), nil
}
return frw.WrappedResponseWriter.Write(b)
}
// WriteHeader sends statusCode to wrapped response writer
func (frw *FallbackResponseWriter) WriteHeader(statusCode int) {
Log.Printf("INFO: WriteHeader called with code %d\n", statusCode)
if statusCode == http.StatusNotFound {
Log.Printf("INFO: Setting FileNotFound flag\n")
frw.FileNotFound = true
return
}
frw.WrappedResponseWriter.WriteHeader(statusCode)
}
// AddFallbackHandler wraps the handler func in another handler func covering authentication
func AddFallbackHandler(handler http.HandlerFunc, filename string) http.HandlerFunc {
Log.Printf("INFO: Creating fallback handler")
return func(w http.ResponseWriter, r *http.Request) {
Log.Printf("INFO: Wrapping response writer in fallback response writer")
frw := FallbackResponseWriter{
WrappedResponseWriter: w,
FileNotFound: false,
}
handler(&frw, r)
if frw.FileNotFound {
Log.Printf("INFO: Serving fallback")
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
}
Run Code Online (Sandbox Code Playgroud)
它可以像这个例子中那样添加(使用 goji 作为 mux):
mux.Handle(pat.Get("/*"),
AddFallbackHandler(http.FileServer(http.Dir("./html")).ServeHTTP, "/"))
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
40927 次 |
最近记录: |