戈朗。用啥? http.ServeFile(..) 还是 http.FileServer(..)?

Posted

技术标签:

【中文标题】戈朗。用啥? http.ServeFile(..) 还是 http.FileServer(..)?【英文标题】:Golang. What to use? http.ServeFile(..) or http.FileServer(..)?戈朗。用什么? http.ServeFile(..) 还是 http.FileServer(..)? 【发布时间】:2015-05-01 20:24:25 【问题描述】:

我有点困惑。许多示例显示了两者的用法: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)

我尝试阅读源代码,它们都使用serveFile(ResponseWriter, *Request, FileSystem, string, bool) 底层函数。但是http.FileServer 使用自己的ServeHTTP() 方法返回fileHandler,并在提供文件之前做一些准备工作(例如path.Clean())。

那么为什么需要这种分离呢?哪种方法更好用?以及如何设置自定义 NotFound 处理程序,例如在未找到请求的文件时?

【问题讨论】:

最大的区别是http.FileServerhttp.Handler,而http.ServeFile不是。 谢谢,我知道,但这并没有让我的困境变得更好) 【参考方案1】:

主要区别在于http.FileServer 有效地将 HTTP 前缀与文件系统进行了几乎 1:1 的映射。用简单的英语来说,它提供了一个完整的目录路径。及其所有的孩子。

假设您有一个名为 /home/bob/static 的目录,并且您进行了以下设置:

fs := http.FileServer(http.Dir("/home/bob/static"))
http.Handle("/static/", http.StripPrefix("/static", fs))

您的服务器会接受请求,例如/static/foo/bar 并提供 /home/bob/static/foo/bar(或 404)处的任何内容

相比之下,ServeFile 是一个较低级别的助手,可用于实现类似于 FileServer 的东西,或者实现你自己的路径修改,以及任何数量的东西。它只是获取命名的本地文件并通过 HTTP 连接发送它。就其本身而言,它不会提供整个目录前缀(除非您编写了一个处理程序来进行类似于 FileServer 的一些查找)

注意天真地为文件系统提供服务是一件有潜在危险的事情(有潜在的方法可以打破根树)因此我建议除非你真的知道什么您正在这样做,请使用 http.FileServerhttp.Dir,因为它们包含检查以确保人们无法突破 FS,而 ServeFile 不会。

附录 不幸的是,你的第二个问题,你如何做一个自定义的 NotFound 处理程序,并不容易回答。因为正如您所注意到的,这是从内部函数 serveFile 调用的,所以没有超级容易的地方可以闯入它。可能会有一些偷偷摸摸的事情,比如用你自己的ResponseWriter 拦截响应,它会拦截 404 响应代码,但我会把这个练习留给你。

【讨论】:

非常非常好!这正是我想听到的!非常感谢!)我的英语不是母语,所以你能告诉我“munging”是什么意思吗?) Munging: en.wikipedia.org/wiki/Data_wrangling .. 所以他基本上是指创建自己的路径映射 甚至en.wikipedia.org/wiki/Mung_(computer_term) "它有时用于说话者尚不清楚的模糊数据转换步骤。[2] 常见的转换操作包括删除标点符号或 html 标签、数据解析、过滤、和转型。” 注意,要在备用 URL 路径 (/tmpfiles/) 下提供磁盘 (/tmp) 上的目录,请确保包含尾部斜杠。例如。上面的示例在/tmp 中使用路由static 提供文件将变为:http.Handle("/static/", http.StripPrefix("/static/", fs)) 关于安全问题的好建议——http.FileServer 对于大多数用例来说可能是最安全的方法。【参考方案2】:

这里是一个处理程序,如果找不到文件,则将重定向发送到“/”。这在为 Angular 应用程序添加回退时会派上用场,正如 here 建议的那样,它是从 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)
            
        
    

可以像本例那样添加(使用 goji 作为 mux):

    mux.Handle(pat.Get("/*"),
        AddFallbackHandler(http.FileServer(http.Dir("./html")).ServeHTTP, "/"))

【讨论】:

以上是关于戈朗。用啥? http.ServeFile(..) 还是 http.FileServer(..)?的主要内容,如果未能解决你的问题,请参考以下文章

一些运算符“|”、“^”、“&”、“&^”之间的区别。戈朗

从 rabbitmq 获取已发布消息的响应。戈朗

戈朗。从包中只导入一个文件

io.Copy write:大文件的管道损坏

对一个容器多次排序,用啥容器,用啥方法

请问用啥软件生成.xml文件,用啥软件生成html文件?