2017-03-22 1 views
3

静的ファイル用の非常に単純な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) 
    }) 
} 

それは(R、W)next.ServeHTTPを延期するためにここに可能でしょうか?このように:私はこのようなnext.ServeHTTP(R、W)を延期する場合は、その意志

router.NotFound = gzipHandler(fileServer()) 

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()関数でこれを使用していますfileServer()が実行される前に実行されますか?関数呼び出しで

+0

はい、deferはすべての終了パスで関数を呼び出します。したがって、可能です。間違いなく明らかではない。代わりに、大量のコードを別のcanGzip(要求)関数に抽出することを検討してください。 – Mark

+0

canGzip(リクエスト)についてのアドバイスをありがとう!別の機能でそれらを包むことができたことは私には起こりませんでした。ありがとう! – Alex

答えて

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)戻る前に呼び出されます。

+0

優秀で徹底した説明! – Adrian

関連する問題