2014-10-01 92 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