Go and Gorilla Mux NotFoundHandler not working - go

I just can't get this NotFoundHandler to work. I'd like to serve a static file on every get request, given that it exists, otherwise serve index.html. Here's my simplified router at the moment:
func fooHandler() http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Foo"))
}
return http.HandlerFunc(fn)
}
func notFound(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "public/index.html")
}
func main() {
router = mux.NewRouter()
fs := http.FileServer(http.Dir("public"))
router.Handle("/foo", fooHandler())
router.PathPrefix("/").Handler(fs)
router.NotFoundHandler = http.HandlerFunc(notFound)
http.ListenAndServe(":3000", router)
}
/foo works ok
/file-that-exists works ok
/file-that-doesnt-exist doesn't work
- I get 404 page not found instead of index.html
So what am I doing wrong here?

The problem is that router.PathPrefix("/").Handler(fs) will match every route and the NotFoundHandler is never executed.
The NotFoundHandler is only executed when the router can't find a matching route.
When you explicitly define your routes it works as expected.
You could do something like:
router.Handle("/foo", fooHandler())
router.PathPrefix("/assets").Handler(fs)
router.HandleFunc("/", index)
router.HandleFunc("/about", about)
router.HandleFunc("/contact", contact)
router.NotFoundHandler = http.HandlerFunc(notFound)

This has worked for me
r.NotFoundHandler = http.HandlerFunc(NotFound)
Makesure your 'NotFound' function has:
func NotFound(w http.ResponseWriter, r *http.Request) { // a * before http.Request

Related

How to remove a named route from Gorilla mux in go? [duplicate]

I am fairly new to Go and have not been able to find any information on this, maybe it is just not possible at this time.
I am trying to delete or replace a mux route (using http.NewServeMux, or gorilla's mux.Router). My end goal is to be able to enable/disable a route or set of routes without having to restart the program.
I can probably accomplish this on a handler to handler basis and just return 404 if that feature is "disabled", but I would rather find a more general way to do this since I would like to implement it for every route in my application.
Or would I be better off just keeping track of disabled url patterns and using some middleware to prevent handler execution?
If someone can at least point me in the right direction, I will absolutely post code examples of a solution assuming there is one. Thanks!
There's no built in way, but it is easy enough to implement play.
type HasHandleFunc interface { //this is just so it would work for gorilla and http.ServerMux
HandleFunc(pattern string, handler func(w http.ResponseWriter, req *http.Request))
}
type Handler struct {
http.HandlerFunc
Enabled bool
}
type Handlers map[string]*Handler
func (h Handlers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if handler, ok := h[path]; ok && handler.Enabled {
handler.ServeHTTP(w, r)
} else {
http.Error(w, "Not Found", http.StatusNotFound)
}
}
func (h Handlers) HandleFunc(mux HasHandleFunc, pattern string, handler http.HandlerFunc) {
h[pattern] = &Handler{handler, true}
mux.HandleFunc(pattern, h.ServeHTTP)
}
func main() {
mux := http.NewServeMux()
handlers := Handlers{}
handlers.HandleFunc(mux, "/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("this will show once"))
handlers["/"].Enabled = false
})
http.Handle("/", mux)
http.ListenAndServe(":9020", nil)
}
Yes you can.
One way to do it is to have a sturct that implement http.Handle interface with the method
ServeHTTP.
Then have the struct contain another muxer like gorilla's
and finally have an atomic Switch to enable/ disable the subrouting
This is a working example of what I mean:
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
"sync/atomic"
)
var recording int32
func isRecording() bool {
return atomic.LoadInt32(&recording) != 0
}
func setRecording(shouldRecord bool) {
if shouldRecord {
atomic.StoreInt32(&recording, 1)
} else {
atomic.StoreInt32(&recording, 0)
}
}
type SwitchHandler struct {
mux http.Handler
}
func (s *SwitchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if isRecording() {
fmt.Printf("Switch Handler is Recording\n")
s.mux.ServeHTTP(w, r)
return
}
fmt.Printf("Switch Handler is NOT Recording\n")
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "NOT Recording\n")
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/success/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Recording\n")
})
handler := &SwitchHandler{mux: router}
setRecording(false)
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
According to https://github.com/gorilla/mux/issues/82 it is suggested to swap the router instead of deleting routes. Existing connections will stay open.

Trying to serve React SPA that uses react-router

Im using Gorilla/Mux for routing and want to serve the React SPA regardless of the URL path.
func main() {
fmt.Println("server running...")
hub := newHub()
go hub.run()
router := mux.NewRouter()
router.HandleFunc("/api/create", Api)
router.HandleFunc("/api/getpoll", Api)
router.HandleFunc("/api/update", Api)
router.HandleFunc("/sockets/{id}", func(w http.ResponseWriter, r
*http.Request) {
Socketme(hub, w, r)
})
// router.HandleFunc("/{rest:.*}", emberHandler)
router.PathPrefix("/").HandlerFunc(serveFile)
log.Fatal(http.ListenAndServe(":5000", router))
}
func serveFile(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("./public/build")).ServeHTTP(w, r)
}
Dont want Go to give 404s the Spa should be handling these routes.
The Router exports a NotFoundHandler field which you can set to your custom handler.
router := mux.NewRouter()
router.NotFoundHandler = MyCustom404Handler
So you could do something like:
router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./public/build/index.html")
}))
so that it always serves your index page when it would normally return a 404
So I couldnt find any workable solutions to this. So I ended up taking a different approach using mux.Subrouter found here Static file server in Golang using gorilla/mux

How to implement case insensitive URL matching using gorilla mux

I need to implement case insensitive URL matching in gorilla mux as it is done here for built in mux
I tried to achieve the same using middle-ware like this
router := mux.NewRouter()
router.Use(srv.GetCaseMiddleware())
//GetCaseMiddleware middleware to make match URL case insensitive
func (srv *Server) GetCaseMiddleware() (w mux.MiddlewareFunc) {
var middleware mux.MiddlewareFunc = func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.ToLower(r.URL.Path)
next.ServeHTTP(w, r)
})
}
return middleware
}
but still it throws 404 if URL case is changed,is there any way to implement it using gorilla-mux
Unfortunately, as of this writing, middleware functions are invoked after URL matching in gorilla/mux.
Mux supports the addition of middlewares to a Router, which are executed in the order they are added if a match is found, including its subrouters.
I would suggest going with the example in the link you provided.
e.g.
func CaselessMatcher(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.ToLower(r.URL.Path)
next.ServeHTTP(w, r)
})
}
Then, just wrap your multiplexer.
r := mux.NewRouter()
//...
handler := CaselessMatcher(r)
It's actually not bad IMO.

Nested Gorilla Mux router does not work

Using code below, when I access /test2 it responds with 404 - not found. /test1 works correctly. Why is that? Is nesting not allowed despite the fact that routers implement http.Handler interface?
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mainRouter := mux.NewRouter()
subRouter := mux.NewRouter()
mainRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test1") })
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test2") })
mainRouter.Handle("/", subRouter)
http.ListenAndServe(":9999", mainRouter)
}
EDIT:
My main goal was to add some initial work which would be common for all routes in subRouter, and only for them. To be even more specific, I would like to use Negroni as my middleware orchiestrator.
On the Negroni website there is an example of adding middleware to the group of routes:
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// add admin routes here
Create a new negroni for the admin middleware
router.Handle("/admin", negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
Negroni basically executes ServeHTTP methods of every argument, since all of them implement http.Handler. It executes them in order, so router routes will be last.
I'm familiar with the concept of Subrouter in Mux, but AFAIK I can't use it in similar fashion as example above, in particular, I can't inject anything between mainRouter and its Subrouter. This is why nesting looks more flexible.
I know this question is somewhat old, but I have spent some time figuring out how handlers and matching work in go. You can see my experiment code here.
Basically, you can get the effect you want with code like this:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mainRouter := mux.NewRouter()
subRouter := mux.NewRouter()
mainRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "test1")
})
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "test2")
})
// in mux, you need to register subrouter
// with the same path that the handlers in
// it are matching
mainRouter.Handle("/test2", subRouter)
// if your subrouter has handlers that match
// other sub paths - you also need to do this
mainRouter.Handle("/test2/{_dummy:.*}", subRouter)
http.ListenAndServe(":9999", mainRouter)
}
I hope this helps someone.
None of the previous answers given helped me achieve exactly what I was seeking out to do. I was trying to use negroni.Wrap() around a Subrouter with lots of routes. I believe that is what the original poster wanted.
Additional Context: I am deploying on Google App Engine, hence putting everything in the init() function.
package hello
import (
"fmt"
"net/http"
"github.com/codegangsta/negroni"
"github.com/gorilla/mux"
)
func init() {
// Create the "root" router, if you will...
r := mux.NewRouter().StrictSlash(true)
// Create your "Subrouter" dedicated to /api which will use the PathPrefix
apiRouter := mux.NewRouter().PathPrefix("/api").Subrouter().StrictSlash(true)
// This step is where we connect our "root" router and our "Subrouter" together.
r.PathPrefix("/api").Handler(negroni.New(
negroni.HandlerFunc(myMiddleware),
negroni.Wrap(apiRouter),
))
// Define "root" routes using r
r.HandleFunc(genHandleFunc("/", "root of site"))
r.HandleFunc(genHandleFunc("/home", "home"))
// Define "Subrouter" routes using apiRouter, prefix is /api
apiRouter.HandleFunc(genHandleFunc("/", "root of API, /api")) // Matches: /api
apiRouter.HandleFunc(genHandleFunc("/v1", "root of API V1, /api/v1")) // Matches: /api/v1
apiRouter.HandleFunc(genHandleFunc("/v1/resourceabc", "API V1 - resourceabc, /api/v1/resourceabc")) // Matches: /api/v1/resourceabc
/* Finally we pass our "root" router to the net/http library. The "root" router will contain all
of the routes for /api also.
*/
http.Handle("/", r)
}
// Silly function to quickly generate a HandleFunc
func genHandleFunc(p string, msg string) (path string, f func(http.ResponseWriter, *http.Request)) {
path = p
f = func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%v\n", msg)
}
return
}
func myMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
fmt.Fprintln(w, "Before doing work...")
next(w, r)
fmt.Fprintln(w, "After doing work...")
}
You wouldn't use two routers here anyway.
Gorilla Mux has the concept of a Subrouter, whereby you define the top level domain properties on the main router, then use the Subrouter instance to map the individual paths.
For example:
mainRouter := mux.NewRouter()
subRouter := mainRouter.PathPrefix("/").Subrouter()
subRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test1") })
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test2") })
mainRouter.Handle("/", mainRouter)
You can go even further than that though - for example, you may have another router at /test1 and a subrouter that matches anything further below that (say /test1/othertest).
Use full path in subroutes:
router := mux.NewRouter()
apiRoutes := mux.NewRouter()
apiRoutes.Handle("/api/auth", Auth)
router.PathPrefix("/api").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(apiRoutes),
))

Showing custom 404 error page with standard http package

Assuming that we have:
http.HandleFunc("/smth", smthPage)
http.HandleFunc("/", homePage)
User sees a plain "404 page not found" when they try a wrong URL. How can I return a custom page for that case?
Update concerning gorilla/mux
Accepted answer is ok for those using pure net/http package.
If you use gorilla/mux you should use something like this:
func main() {
r := mux.NewRouter()
r.NotFoundHandler = http.HandlerFunc(notFound)
}
And implement func notFound(w http.ResponseWriter, r *http.Request) as you want.
I usually do this:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/smth/", smthHandler)
http.ListenAndServe(":12345", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
errorHandler(w, r, http.StatusNotFound)
return
}
fmt.Fprint(w, "welcome home")
}
func smthHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/smth/" {
errorHandler(w, r, http.StatusNotFound)
return
}
fmt.Fprint(w, "welcome smth")
}
func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
w.WriteHeader(status)
if status == http.StatusNotFound {
fmt.Fprint(w, "custom 404")
}
}
Here I've simplified the code to only show custom 404, but I actually do more with this setup: I handle all the HTTP errors with errorHandler, in which I log useful information and send email to myself.
Following is the approach I choose. It is based on a code snippet which I cannot acknowledge since I lost the browser bookmark.
Sample code : (I put it in my main package)
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)
}
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)
})
}
func fire404(res http.ResponseWriter, req *http.Request) bool{
fmt.Fprintf(res, "File not found. Please check to see if your URL is correct.");
return true;
}
func main(){
handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));
var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);
http.Handle("/static/", v_blessed_handler_statics);
// add other handlers using http.Handle() as necessary
if err := http.ListenAndServe(":8080", nil); err != nil{
log.Fatal("ListenAndServe: ", err);
}
}
Please customize the func fire404 to output your own version of message for error 404.
If you happen to be using Gorilla Mux, you may wish to replace the main function with below :
func main(){
handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));
var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);
r := mux.NewRouter();
r.PathPrefix("/static/").Handler(v_blessed_handler_statics);
// add other handlers with r.HandleFunc() if necessary...
http.Handle("/", r);
log.Fatal(http.ListenAndServe(":8080", nil));
}
Please kindly correct the code if it is wrong, since I am only a newbie to Go. Thanks.
Ancient thread, but I just made something to intercept http.ResponseWriter, might be relevant here.
package main
//GAE POC originally inspired by https://thornelabs.net/2017/03/08/use-google-app-engine-and-golang-to-host-a-static-website-with-same-domain-redirects.html
import (
"net/http"
)
func init() {
http.HandleFunc("/", handler)
}
// HeaderWriter is a wrapper around http.ResponseWriter which manipulates headers/content based on upstream response
type HeaderWriter struct {
original http.ResponseWriter
done bool
}
func (hw *HeaderWriter) Header() http.Header {
return hw.original.Header()
}
func (hw *HeaderWriter) Write(b []byte) (int, error) {
if hw.done {
//Silently let caller think they are succeeding in sending their boring 404...
return len(b), nil
}
return hw.original.Write(b)
}
func (hw *HeaderWriter) WriteHeader(s int) {
if hw.done {
//Hmm... I don't think this is needed...
return
}
if s < 400 {
//Set CC header when status is < 400...
//TODO: Use diff header if static extensions
hw.original.Header().Set("Cache-Control", "max-age=60, s-maxage=2592000, public")
}
hw.original.WriteHeader(s)
if s == 404 {
hw.done = true
hw.original.Write([]byte("This be custom 404..."))
}
}
func handler(w http.ResponseWriter, r *http.Request) {
urls := map[string]string{
"/example-post-1.html": "https://example.com/post/example-post-1.html",
"/example-post-2.html": "https://example.com/post/example-post-2.html",
"/example-post-3.html": "https://example.com/post/example-post-3.html",
}
w.Header().Set("Strict-Transport-Security", "max-age=15768000")
//TODO: Put own logic
if value, ok := urls[r.URL.Path]; ok {
http.Redirect(&HeaderWriter{original: w}, r, value, 301)
} else {
http.ServeFile(&HeaderWriter{original: w}, r, "static/"+r.URL.Path)
}
}
i think the clean way is this:
func main() {
http.HandleFunc("/calculator", calculatorHandler)
http.HandleFunc("/history", historyHandler)
http.HandleFunc("/", notFoundHandler)
log.Fatal(http.ListenAndServe(":80", nil))
}
if the address is not /calulator or /history, then it handles notFoundHandler function.
Maybe I'm wrong, but I just checked the sources: http://golang.org/src/pkg/net/http/server.go
It seems like specifying custom NotFound() function is hardly possible: NotFoundHandler() returns a hardcoded function called NotFound().
Probably, you should submit an issue on this.
As a workaround, you can use your "/" handler, which is a fallback if no other handlers were found (as it is the shortest one). So, check is page exists in that handler and return a custom 404 error.
You just need to create your own notFound handler and register it with HandleFunc for the path that you don't handle.
If you want the most control over your routing logic you will need to use a custom server and custom handler type of your own.
http://golang.org/pkg/net/http/#Handler
http://golang.org/pkg/net/http/#Server
This allows you to implement more complex routing logic than the HandleFunc will allow you to do.
you can define
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
if request.URL.Path != "/" {
writer.WriteHeader(404)
writer.Write([]byte(`not found, da xiong dei !!!`))
return
}
})
when access not found resource, it will execute to http.HandleFunc("/", xxx)
You can simply use something like:
func Handle404(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "404 error\n")
}
func main(){
http.HandleFunc("/", routes.Handle404)
}
If you need to get the standard one, just write:
func main(){
http.HandleFunc("/", http.NotFound)
}
And you'll get:
404 page not found

Resources