This is my web server code ::
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
func FooHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
pathVal := vars["item"]
fmt.Printf("\n item :: %s \n", pathVal)
w.Write([]byte("Gorilla!\n"))
}
func main() {
router := mux.NewRouter()
routes := router.PathPrefix("/{item}").Subrouter()
routes.Methods(http.MethodGet).
Path("/foo").
HandlerFunc(FooHandler)
log.Fatal(http.ListenAndServe(":8000", router))
}
Everything works perfectly when I send requests via Postman to URLs like 127.0.0.1:8000/itemvalue/foo
- the request reaches FooHandler
, i get the correct value in pathVal
.
What i need is for requests like 127.0.0.1:8000//foo
(an empty string in place of item
in URL path, so there are two slashes) to behave exactly the same. I want the request to reach the FooHandler
, and pathVal
to be empty string. However, I am currently getting a 404 page not found
error; the request doesn't reach FooHandler
.
I tried this ::
routes := router.PathPrefix("/{item:^$|.+}").Subrouter()
This regular expression should match an empty string or any string, but i still get "404 page not found" error.
The problem is, as @BMitch correctly pointed out, that the default mux as well as Gorilla normalize paths and will send you a "301 Moved Permanently" response.
So, what you have to do is to move the error handling to a point where neither of them is involved. You can do this by wrapping the actual mux in a handler which deals with the invalid URL the way you want:
package main
import (
"fmt"
"log"
"net/http"
"regexp"
"github.com/gorilla/mux"
)
var (
// doubleSlashInFoo is a regex to match the URL path with double slashes in
// the beginning and Ending with "//foo"
// This, of course, can be adjusted to match any other pattern you want.
doubleSlashInFoo = regexp.MustCompile(`^//foo$`)
)
func FooHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
pathVal := vars["item"]
fmt.Printf("\n item :: %s \n", pathVal)
w.Write([]byte("Gorilla!\n"))
}
// specialNotFoundHandler is a custom handler that checks for double slashes in
// the URL path and returns a 400 Bad Request error if found. It also serves
// the request to the underlying mux router. Only there the double slashes will cause
// a "301 Moved Permanently" reponse.
type specialNotFoundHandler struct {
mux http.Handler
}
// ServeHTTP implements the http.Handler interface for specialNotFoundHandler.
func (h *specialNotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if doubleSlashInFoo.MatchString(r.URL.Path) {
http.Error(w, "The specified bucket is not valid.", http.StatusBadRequest)
return
}
h.mux.ServeHTTP(w, r)
}
func main() {
router := mux.NewRouter()
routes := router.PathPrefix("/{item}").Subrouter()
routes.Methods(http.MethodGet).
Path("/foo").
HandlerFunc(FooHandler)
log.Fatal(http.ListenAndServe(":8000", &specialNotFoundHandler{mux: router}))
}
Calling this returns the expected response:
$ curl -v http://localhost:8000//foo
* Host localhost:8000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8000...
* Connected to localhost (::1) port 8000
> GET //foo HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 400 Bad Request
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Thu, 27 Mar 2025 17:26:36 GMT
< Content-Length: 35
<
The specified bucket is not valid.
You might also want to have a look at @Thundercat's answer to a similar question where they describe a normalizer to prevent a "301 Moved Permanently" response.