英文:
What happens if I defer a function inside a function that returns a function? How is the ordering?
问题
我正在尝试创建一个非常简单的用于静态文件的gzip中间件。但是在代码中有5个不同的地方调用了next.ServeHTTP(w, r)
,如果我使用defer关键字,会发生什么?这个语句会在返回的函数运行之前被调用吗?
这是我的代码:
func gzipHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
// 如果客户端不理解gzip,则继续执行。
next.ServeHTTP(w, r)
return
}
path := filepath.FromSlash(filepath.Join(cfg.PublicHTML, r.URL.Path))
if _, err := os.Stat(path); os.IsNotExist(err) {
// 如果文件或文件夹不存在,则继续执行。
next.ServeHTTP(w, r)
return
}
var ext string
for _, v := range cfg.GzipExt {
if strings.HasSuffix(r.URL.Path, v) {
ext = v
}
}
if ext == "" {
// 该文件不应作为gzip内容提供。
next.ServeHTTP(w, r)
return
}
// 仅在存在时提供gzip文件。
if _, err := os.Stat(path + ".gz"); os.IsNotExist(err) {
// TODO: 创建gzip文件。
// http://stackoverflow.com/questions/16890648/how-can-i-use-golangs-compress-gzip-package-to-gzip-a-file
next.ServeHTTP(w, r)
return
}
w.Header().Add("Content-Encoding", "gzip")
r.URL.Path = r.URL.Path + ".gz"
next.ServeHTTP(w, r)
})
}
在这里使用defer next.ServeHTTP(w, r)
是否可行?像这样:
func gzipHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer next.ServeHTTP(w, r)
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
// 如果客户端不理解gzip,则继续执行。
return
}
path := filepath.FromSlash(filepath.Join(cfg.PublicHTML, r.URL.Path))
if _, err := os.Stat(path); os.IsNotExist(err) {
// 如果文件或文件夹不存在,则继续执行。
return
}
var ext string
for _, v := range cfg.GzipExt {
if strings.HasSuffix(r.URL.Path, v) {
ext = v
}
}
if ext == "" {
// 该文件不应作为gzip内容提供。
return
}
// 仅在存在时提供gzip文件。
if _, err := os.Stat(path + ".gz"); os.IsNotExist(err) {
// TODO: 创建gzip文件。
// http://stackoverflow.com/questions/16890648/how-can-i-use-golangs-compress-gzip-package-to-gzip-a-file
return
}
w.Header().Add("Content-Encoding", "gzip")
r.URL.Path = r.URL.Path + ".gz"
})
}
我在main()函数中这样使用它来提供静态文件:
router.NotFound = gzipHandler(fileServer())
如果我像这样使用defer next.ServeHTTP(w, r)
,它会在fileServer()执行之前被执行吗?
英文:
I am trying to create a very simple gzip middleware for static files. But I am calling next.ServeHTTP(w, r)
5 different places in the code, what happens if I defer this? Will this be called before the function that is returned is run?
This is what I have:
func gzipHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
// If for some weird reason client does not understand gzip, then continue.
next.ServeHTTP(w, r)
return
}
path := filepath.FromSlash(filepath.Join(cfg.PublicHTML, r.URL.Path))
if _, err := os.Stat(path); os.IsNotExist(err) {
// If file or folder does not exists, then continue.
next.ServeHTTP(w, r)
return
}
var ext string
for _, v := range cfg.GzipExt {
if strings.HasSuffix(r.URL.Path, v) {
ext = v
}
}
if ext == "" {
// This file should not be served as gzipped content.
next.ServeHTTP(w, r)
return
}
// Only serve gzipped file if it exists.
if _, err := os.Stat(path + ".gz"); os.IsNotExist(err) {
// TODO: Create the gzipped file.
// http://stackoverflow.com/questions/16890648/how-can-i-use-golangs-compress-gzip-package-to-gzip-a-file
next.ServeHTTP(w, r)
return
}
w.Header().Add("Content-Encoding", "gzip")
r.URL.Path = r.URL.Path + ".gz"
next.ServeHTTP(w, r)
})
}
Would it be possible here to defer next.ServeHTTP(w, r)? Like this:
func gzipHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer next.ServeHTTP(w, r)
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
// If for some weird reason client does not understand gzip, then continue.
return
}
path := filepath.FromSlash(filepath.Join(cfg.PublicHTML, r.URL.Path))
if _, err := os.Stat(path); os.IsNotExist(err) {
// If file or folder does not exists, then continue.
return
}
var ext string
for _, v := range cfg.GzipExt {
if strings.HasSuffix(r.URL.Path, v) {
ext = v
}
}
if ext == "" {
// This file should not be served as gzipped content.
return
}
// Only serve gzipped file if it exists.
if _, err := os.Stat(path + ".gz"); os.IsNotExist(err) {
// TODO: Create the gzipped file.
// http://stackoverflow.com/questions/16890648/how-can-i-use-golangs-compress-gzip-package-to-gzip-a-file
return
}
w.Header().Add("Content-Encoding", "gzip")
r.URL.Path = r.URL.Path + ".gz"
})
}
I am using this in my main() function like this to serve static files:
router.NotFound = gzipHandler(fileServer())
If I defer next.ServeHTTP(w, r) like this, will it be executed before fileServer() is executed?
答案1
得分: 6
golang 规范
在函数调用中,函数值和参数按照通常的顺序进行求值。在它们求值之后,调用的参数按值传递给函数,并且被调用的函数开始执行。
gzipHandler(fileServer())
类似于这样:
a := fileServer()
gzipHandler(a)
所以,显然 fileServer()
首先被执行。
但我认为你困惑的是 defer
语句何时执行,对吗?
根据规范
每次执行 "defer" 语句时,函数值和调用的参数按照通常的方式进行求值,并且保存为新值,但实际的函数不会被调用。相反,延迟函数会在包围它的函数返回之前立即被调用,按照它们被延迟的相反顺序。如果延迟函数值求值为 nil,在调用函数时会引发 panic,而不是在执行 "defer" 语句时。
下面是一个例子来解释:
func t() {
i := 1
defer fmt.Println("first defer:", i)
defer func() {
fmt.Println("second defer:", i)
}()
i = 2
fmt.Println("t return")
}
t()
将打印:
t return
second defer: 2
first defer: 1
在你的代码中,延迟函数 "next.ServeHTTP
" 在匿名函数 func(w http.ResponseWriter, r *http.Request)
返回之前被调用。
英文:
golang spec
> In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.
gzipHandler(fileServer())
is somewhat like this:
a:=fileServer()
gzipHandler(a)
So, obviously fileServer()
is executed first.
but i think what confuse you is when will defer
statement execute, right?
according to the spec
> Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed.
an example to explain:
func t() {
i := 1
defer fmt.Println("first defer:", i)
defer func() {
fmt.Println("second defer:", i)
}()
i = 2
fmt.Println("t return")
}
t()
will print:
t return
second defer: 2
first defer: 1
in your code, the deferred function "next.ServeHTTP
" are invoked before anonymous function func(w http.ResponseWriter, r *http.Request)
returns.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论