2014-10-01 57 views
3

我有以下代碼,一切工作正常。自定義404與大猩猩Mux和標準http.FileServer

var view404 = template.Must(template.ParseFiles("views/404.html")) 

func NotFound(w http.ResponseWriter, r *http.Request) { 
    w.WriteHeader(404) 
    err := view404.Execute(w, nil) 
    check(err) 
} 

func main() { 
    router := mux.NewRouter() 
    router.StrictSlash(true) 
    router.NotFoundHandler = http.HandlerFunc(NotFound) 
    router.Handle("/", IndexHandler).Methods("GET") 
    router.PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir("public")))) 
    http.Handle("/", router) 
    http.ListenAndServe(":8000", nil) 
} 

對像/cannot/find這樣的路線的請求顯示我的自定義404模板。我的/public/目錄中的所有靜態文件也可以正常使用。

我有一個問題處理不存在的靜態文件,並顯示我的自定義NotFound他們的處理程序。到/public/cannot/find請求調用標準http.NotFoundHandler與

404頁的答覆沒有找到

我怎麼能有正常的路由和靜態文件相同的自定義NotFoundHandler?


更新

我最終通過纏繞http.ServeFile爲@Dewy布羅託建議實施我自己FileHandler

type FileHandler struct { 
    Path string 
} 

func (f FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    prefix := "public" 
    http.ServeFile(w, r, path.Join(prefix, f.Path)) 
} 

// ... 

router.Handle("/css/styles.css", FileHandler{"/css/styles.css"}).Methods("GET") 

現在我的NotFound處理程序捕獲所有丟失的路由,甚至丟失文件。

+0

Eek。現在你需要一個處理器來處理你想要的每個文件。圖片,JavaScript等......我更喜歡他的鉤子解決方案,但即便如此,這是一個簡單的目標很多代碼。 – 2015-11-18 16:37:08

回答

5

FileServer正在生成404響應。 FileServer處理多路複用器傳遞給它的所有請求,包括丟失文件的請求。有幾種方法可以使用自定義404頁面來提供靜態文件:

  • 使用ServeContent編寫您自己的文件處理程序。該處理程序可以以任何您想要的方式生成錯誤響應。如果您不生成索引頁,則代碼不是很多。
  • 使用另一個掛鉤傳遞給FileHandler的ResponseWriter的處理程序包裝FileServer處理程序。當調用時,鉤子寫入不同的主體。
  • 使用多路複用器註冊每個靜態資源,以便未發現錯誤由多路複用器中的catchall處理。這種方法需要一個簡單的包裝ServeFile

下面是第二種方法所描述的包裝的草圖:

type hookedResponseWriter struct { 
    http.ResponseWriter 
    ignore bool 
} 

func (hrw *hookedResponseWriter) WriteHeader(status int) { 
    hrw.ResponseWriter.WriteHeader(status) 
    if status == 404 { 
     hrw.ignore = true 
     // Write custom error here to hrw.ResponseWriter 
    } 
} 

func (hrw *hookedResponseWriter) Write(p []byte) (int, error) { 
    if hrw.ignore { 
     return len(p), nil 
    } 
    return hrw.ResponseWriter.Write(p) 
} 

type NotFoundHook struct { 
    h http.Handler 
} 

func (nfh NotFoundHook) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    nfh.h.ServeHTTP(&hookedResponseWriter{ResponseWriter: w}, r) 
} 

通過包裝文件服務器使用掛鉤:

這種簡單的鉤
router.PathPrefix("/public/").Handler(NotFoundHook{http.StripPrefix("/public/", http.FileServer(http.Dir("public")))}) 

一個需要注意的是,它的塊服務器中用於從文件複製到套接字的優化。

+0

你能展示一些最小的示例代碼嗎? – zemirco 2014-10-01 13:36:06

+0

方法2會很棒。 – zemirco 2014-10-01 13:40:36

+0

謝謝!我選擇了方法3,因爲它使我能夠最大限度地控制所服務的內容。我沒有那麼多的靜態文件,他們可能會弄亂我的代碼。看到我上面的更新。 – zemirco 2014-10-01 14:19:57