douchuilai2355
2017-03-22 02:32
浏览 37
已采纳

如果我在返回一个函数的函数中延迟一个函数会怎样? 订购如何?

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条回答 默认 最新

  • doubu4826 2017-03-22 03:17
    已采纳

    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.

    已采纳该答案
    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题