admin 管理员组

文章数量: 1086019

This is my web server code ::

package main

import (
    "fmt"
    "log"
    "net/http"

    "github/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.

This is my web server code ::

package main

import (
    "fmt"
    "log"
    "net/http"

    "github/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.

Share Improve this question edited Mar 27 at 10:41 TypeAny Word asked Mar 27 at 10:15 TypeAny WordTypeAny Word 133 bronze badges 3
  • 127.0.0.1:8000//foo normalizes to 127.0.0.1:8000/foo. There's no empty field between two slashes, there's only a single slash. – BMitch Commented Mar 27 at 14:00
  • Aside from that: I guess we have a XY problem here. WHY do you need that behavior? – Markus W Mahlberg Commented Mar 27 at 15:37
  • @MarkusWMahlberg to repeat AWS api behavior when bucket is not presented in http path. For example AWS request PUT s3.amazonaws/{{bucket}}/?policy returns 400 and "The specified bucket is not valid.' text in xml response body when request is like s3.amazonaws//?policy . My web server returns 404 with no message. – TypeAny Word Commented Mar 27 at 16:06
Add a comment  | 

1 Answer 1

Reset to default 0

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/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.

本文标签:

Error[2]: Invalid argument supplied for foreach(), File: /www/wwwroot/roclinux.cn/tmp/view_template_quzhiwa_htm_read.htm, Line: 58
File: /www/wwwroot/roclinux.cn/tmp/route_read.php, Line: 205, include(/www/wwwroot/roclinux.cn/tmp/view_template_quzhiwa_htm_read.htm)
File: /www/wwwroot/roclinux.cn/tmp/index.inc.php, Line: 129, include(/www/wwwroot/roclinux.cn/tmp/route_read.php)
File: /www/wwwroot/roclinux.cn/index.php, Line: 29, include(/www/wwwroot/roclinux.cn/tmp/index.inc.php)