如果我在返回函数的函数中推迟函数会发生什么?排序如何?

时间:2017-03-22 02:32:35

标签: go

我正在尝试为静态文件创建一个非常简单的gzip中间件。但是我在代码中调用了next.ServeHTTP(w, r) 5个不同的位置,如果我推迟这个会发生什么?在运行返回的函数之前是否会调用它?

这就是我所拥有的:

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)
    })
}

这里是否可以推迟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") {
            // 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"
    })
}

我在我的main()函数中使用它来提供静态文件:

router.NotFound = gzipHandler(fileServer())

如果我按照这样延迟next.ServeHTTP(w,r),它会在执行fileServer()之前执行吗?

1 个答案:

答案 0 :(得分:5)

golang spec

  

在函数调用中,函数值和参数按通常顺序计算。在评估它们之后,调用的参数通过值传递给函数,并且被调用的函数开始执行。

gzipHandler(fileServer())有点像这样:

a:=fileServer()
gzipHandler(a)

因此,显然首先执行fileServer()

但我认为让你感到困惑的是defer语句何时执行,对吧?

根据spec

  

每次执行“延迟”语句时,将像往常一样评估调用的函数值和参数,并重新保存但不调用实际函数。而是在周围函数返回之前立即调用延迟函数,延迟函数以相反的顺序调用。如果延迟函数值的计算结果为nil,则在调用函数时执行会发生混乱,而不是在执行“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)”。