please add ability to set custom 404 notFoundHandler for http.FileServer

3,406 views
Skip to first unread message

Vasiliy Tolstov

unread,
Apr 20, 2012, 11:21:29 AM4/20/12
to golang-nuts
Hello golang!

I'm try to use http.FileServer for static files like ico, jpg and
other. And i need to set my own 404 error page for absent files.
Please, add like in http.NotFoundHandler ability to set own function for error.
(Now i need to copy/paste some parts from
golang.org/src/pkg/net/http/fs.go to create own handler...
Thanks!

--
Vasiliy Tolstov,
Clodo.ru
e-mail: v.to...@selfip.ru
jabber: va...@selfip.ru

Brad Fitzpatrick

unread,
Apr 20, 2012, 11:33:13 AM4/20/12
to Vasiliy Tolstov, golang-nuts
You can also just wrap the ResponseWriter (it's just an interface) to intercept 404 statuses from the FileServer and write your own 404 message instead.

Vasiliy Tolstov

unread,
Apr 20, 2012, 11:41:51 AM4/20/12
to Brad Fitzpatrick, golang-nuts
2012/4/20 Brad Fitzpatrick <brad...@golang.org>:

> You can also just wrap the ResponseWriter (it's just an interface) to
> intercept 404 statuses from the FileServer and write your own 404 message
> instead.
>

Hm.. Thanks! Can You provide some minimal example for that?

Brad Fitzpatrick

unread,
Apr 20, 2012, 11:44:04 AM4/20/12
to Vasiliy Tolstov, golang-nuts
On Fri, Apr 20, 2012 at 8:41 AM, Vasiliy Tolstov <v.to...@selfip.ru> wrote:
2012/4/20 Brad Fitzpatrick <brad...@golang.org>:
> You can also just wrap the ResponseWriter (it's just an interface) to
> intercept 404 statuses from the FileServer and write your own 404 message
> instead.
>

Hm.. Thanks! Can You provide some minimal example for that?

Sorry, I can't think of an existing one I could link you to.

Andrew Bonventre

unread,
Sep 11, 2013, 7:13:42 PM9/11/13
to golan...@googlegroups.com
Brad,
What about defining NotFoundHandler as an exported var of the package? The internals wouldn’t need to change, but custom 404 pages and behavior could be integrated fairly easily.

If you replace http://golang.org/src/pkg/net/http/server.go?s=33810:33840#L1166 with var NotFoundHandler func() http.Handler

And set the default handler within the init function?

Am I missing something (besides the 1.2 cutoff restrictions)?

A

minux

unread,
Sep 11, 2013, 7:21:11 PM9/11/13
to Andrew Bonventre, golang-nuts

sorry, this will break the Go 1 API promise.

besides, once it's a var, changing it  would affect other uses of the handler globally.

Craig Mason-Jones

unread,
Sep 12, 2013, 12:42:51 AM9/12/13
to golan...@googlegroups.com
I had the same issue, and had been doing a workaround by checking the URL before passing to FileServer, but of course that isn't great - too many static files, too few 404's. Here's an implementation overriding the WriteHeader of http.ResponseWriter:


    type hijack404 struct {
        http.ResponseWriter
        R *http.Request
        Handle404 func (w http.ResponseWriter, r *http.Request) bool
    }

    func (h *hijack404) WriteHeader(code int) {
        if 404==code && h.Handle404(h.ResponseWriter, h.R) {
            panic(h)
        }
        h.ResponseWriter.WriteHeader(code)
    }

    // Handle404 will pass any 404's from the handler to the handle404
    // function. If handle404 returns true, the response is considered complete,
    // and the processing by handler is aborted.
    func Handle404(handler http.Handler, handle404 func (w http.ResponseWriter, r *http.Request) bool) http.Handler {
        return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
            hijack := &hijack404{ ResponseWriter:w, R: r, Handle404: handle404 }
            defer func() {
                if p:=recover(); p!=nil {
                    if p==hijack {
                        return
                    }
                    panic(p)
                }
            }()
            handler.ServeHTTP(hijack, r)
        })
    }

It works with the FileServer, and I suppose should work with other handlers as well, as long as they call WriteHeader before any other output methods. Any comments and feedback welcome.

All the best,
Craig

dan....@gmail.com

unread,
Jan 2, 2014, 2:56:46 PM1/2/14
to golan...@googlegroups.com
I'm using gorillia pat with FileServer and cannot for the life of me figure out why I'm getting a 404, 

 r.Add("GET","/media/",http.StripPrefix("/media/", http.FileServer(http.Dir(DownloadDir))))

still kind of new at the golang and http, how would i use what you have done here to debug my issue.

DisposaBoy

unread,
Jan 3, 2014, 2:42:43 AM1/3/14
to golan...@googlegroups.com
A wild guess: you shouldn't be stripping the trailing slash

dan....@gmail.com

unread,
Jan 3, 2014, 7:06:09 AM1/3/14
to golan...@googlegroups.com

No,

I after looking through the FileServer code I found my problem, I'm on windows, and I was trying to use a the windows \ in the path, not the unix / in the path,
but there was a comment in the FileServer code that stats the path should always be / not matter what OS.

Jim Fisk

unread,
Jan 13, 2023, 5:44:13 PM1/13/23
to golang-nuts
func main() {
    fs := FileServerWith404(http.Dir("my_static_files"))
    http.Handle("/", http.StripPrefix("/", fs))
    http.ListenAndServe(":3000")
}

func FileServerWith404(root http.FileSystem) http.Handler {
    fs := http.FileServer(root)
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        f, err := root.Open(r.URL.Path)
        if err != nil && os.IsNotExist(err) {
            // Make sure you actually have an HTML file at my_static_files/404/index.html
            r.URL.Path = "/404"
        }
        if err == nil {
            f.Close()
        }
        fs.ServeHTTP(w, r)
    })
}
Reply all
Reply to author
Forward
0 new messages