Go语言中间件框架 Negroni 的静态文件处理源码分析
source link: https://studygolang.com/articles/15269?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Negroni是一个非常棒的中间件,尤其是其中间件调用链优雅的设计,以及对GO HTTP 原生处理器的兼容。我以前写过两篇文章,对Negroni进行了专门的分析,没有看过的朋友可以在看下。
Go语言经典库使用分析(五)| Negroni 中间件(一) http://www.flysnow.org/2017/08/20/go-classic-libs-negroni-one.html
Go语言经典库使用分析(六)| Negroni 中间件(二) http://www.flysnow.org/2017/09/02/go-classic-libs-negroni-two.html
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
或者网站 http://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.Copy
, http.ServeContent
可以自动计算响应的内容长度、可以自动识别内容的MIME类型,还可以处理If-Match,If-Unmodified-Since,If-None-Match,If-Modified-Since和If-Range的要求。
因为 * os.File
实现了 io.ReadSeeker
接口,所以我们可以直接使用文件的内容。
小结
好了,到了这里,我们已经分析完了Negroni中静态文件处理中间件的实现,看完后相信你也可以写自己的静态文件处理服务了,可以自己试试,写一个和 http.FileServer
类似功能的静态文件处理服务。
本文为原创文章,转载注明出处,欢迎扫码关注公众号 flysnow_org
或者网站 http://www.flysnow.org/ ,第一时间看后续精彩文章。觉得好的话,顺手分享到朋友圈吧,感谢支持。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK