dongsi2317 2014-10-01 12:58
浏览 94
已采纳

使用Gorilla Mux和std http.FileServer的自定义404

I have the following code and everything works fine.

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

A request to a route like /cannot/find shows my custom 404 template. All static files inside my /public/ directory are also properly served.

I have a problem handling non-existent static files and showing my custom NotFound handler for them. A request to /public/cannot/find calls the standard http.NotFoundHandler which replies with

404 page not found

How can I have the same custom NotFoundHandler for normal routes and static files?


Update

I ended up implementing my own FileHandler by wrapping http.ServeFile as @Dewy Broto suggested.

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

Now my NotFound handler catches all missing routes and even missing files.

  • 写回答

1条回答 默认 最新

  • doutan5337 2014-10-01 13:26
    关注

    The FileServer is generating the 404 response. The FileServer handles all requests passed to it by the mux including requests for missing files. There are a few ways to to serve static files with a custom 404 page:

    • Write your own file handler using ServeContent. This handler can generate error responses in whatever way you want. It's not a lot of code if you don't generate index pages.
    • Wrap the FileServer handler with another handler that hooks the ResponseWriter passed to the FileHandler. The hook writes a different body when WriteHeader(404) is called.
    • Register each static resource with the mux so that not found errors are handled by the catchall in the mux. This approach requires a simple wrapper around ServeFile.

    Here's a sketch of the wrapper described in the second approach:

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

    Use the hook by wrapping the FileServer:

     router.PathPrefix("/public/").Handler(NotFoundHook{http.StripPrefix("/public/", http.FileServer(http.Dir("public")))})
    

    One caveat of this simple hook is that it blocks an optimization in the server for copying from a file to a socket.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看