Go version
go version go1.26.1 linux/amd64
GoFrame version
v2.10.0
Can this bug be reproduced with the latest release?
Option Yes
What did you do?
Using a lowercase HTTP method (e.g., get) to send a request via http.NewRequest("get", ...) to a route that is registered with an exact method match (e.g., GET:/api/user):
req, _ := http.NewRequest("get", "http://127.0.0.1:8000/api/user", nil)
resp, _ := http.DefaultClient.Do(req)
What did you see happen?
The first request using a lowercase get returns 404 Not Found.
Subsequent normal GET requests to the same path will permanently return 404, even when using other HTTP clients (such as curl or Postman).
The issue can only be resolved by restarting the service.
package main
import (
"fmt"
"io"
"net/http"
"time"
"github.com/gogf/gf/v2/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s.BindHandler("GET:/api/user", func(r *ghttp.Request) {
r.Response.Write("hello")
})
s.SetPort(8002)
go s.Run()
time.Sleep(2 * time.Second)
addr := "http://127.0.0.1:8002/api/user"
fmt.Println("=== Request 1: lowercase get ===")
req1, _ := http.NewRequest("get", addr, nil)
resp1, _ := http.DefaultClient.Do(req1)
body1, _ := io.ReadAll(resp1.Body)
fmt.Printf("Status: %d, Body: %s\n", resp1.StatusCode, string(body1))
resp1.Body.Close()
fmt.Println("\n=== Request 2: uppercase GET ===")
req2, _ := http.NewRequest("GET", addr, nil)
resp2, _ := http.DefaultClient.Do(req2)
body2, _ := io.ReadAll(resp2.Body)
fmt.Printf("Status: %d, Body: %s\n", resp2.StatusCode, string(body2))
resp2.Body.Close()
fmt.Println("\n=== Request 3: uppercase GET (again) ===")
req3, _ := http.NewRequest("GET", addr, nil)
resp3, _ := http.DefaultClient.Do(req3)
body3, _ := io.ReadAll(resp3.Body)
fmt.Printf("Status: %d, Body: %s\n", resp3.StatusCode, string(body3))
resp3.Body.Close()
}
output:
2026-04-30T14:53:29.446+08:00 [INFO] pid[13742]: http server started listening on [:8002]
2026-04-30T14:53:29.446+08:00 [INFO] openapi specification is disabled
----------|--------|-----------|-----------------|-------------
| ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE |
----------|--------|-----------|-----------------|-------------
| :8002 | GET | /api/user | main.main.func1 | |
----------|--------|-----------|-----------------|-------------
=== Request 1: lowercase get ===
Status: 404, Body: Not Found
=== Request 2: uppercase GET ===
Status: 404, Body: Not Found
=== Request 3: uppercase GET (again) ===
Status: 404, Body: Not Found
package main
import (
"fmt"
"io"
"net/http"
"time"
"github.com/gogf/gf/v2/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s.BindHandler("GET:/api/user", func(r *ghttp.Request) {
r.Response.Write("hello")
})
s.SetPort(8002)
go s.Run()
time.Sleep(2 * time.Second)
addr := "http://127.0.0.1:8002/api/user"
fmt.Println("=== Request 1: lowercase get ===")
req1, _ := http.NewRequest("GET", addr, nil)
resp1, _ := http.DefaultClient.Do(req1)
body1, _ := io.ReadAll(resp1.Body)
fmt.Printf("Status: %d, Body: %s\n", resp1.StatusCode, string(body1))
resp1.Body.Close()
fmt.Println("\n=== Request 2: uppercase GET ===")
req2, _ := http.NewRequest("get", addr, nil)
resp2, _ := http.DefaultClient.Do(req2)
body2, _ := io.ReadAll(resp2.Body)
fmt.Printf("Status: %d, Body: %s\n", resp2.StatusCode, string(body2))
resp2.Body.Close()
fmt.Println("\n=== Request 3: uppercase GET (again) ===")
req3, _ := http.NewRequest("GET", addr, nil)
resp3, _ := http.DefaultClient.Do(req3)
body3, _ := io.ReadAll(resp3.Body)
fmt.Printf("Status: %d, Body: %s\n", resp3.StatusCode, string(body3))
resp3.Body.Close()
}
output:
2026-04-30T14:54:46.087+08:00 [INFO] pid[13890]: http server started listening on [:8002]
2026-04-30T14:54:46.087+08:00 [INFO] openapi specification is disabled
----------|--------|-----------|-----------------|-------------
| ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE |
----------|--------|-----------|-----------------|-------------
| :8002 | GET | /api/user | main.main.func1 | |
----------|--------|-----------|-----------------|-------------
=== Request 1: lowercase get ===
Status: 200, Body: hello
=== Request 2: uppercase GET ===
Status: 200, Body: hello
=== Request 3: uppercase GET (again) ===
Status: 200, Body: hello
What did you expect to see?
A lowercase get should be treated as equivalent to GET, correctly matching the route and returning 200 (HTTP method names are case-insensitive per RFC specifications).
Even if lowercase methods are not supported, they should not pollute the cache and cause subsequent valid requests to permanently return 404.
The best fix is to normalize the HTTP method at the entry point of getHandlersWithCache:
var (
ctx = r.Context()
method = r.Method
path = r.URL.Path
host = r.GetHost()
)
Change it to:
var (
ctx = r.Context()
method = strings.ToUpper(r.Method)
path = r.URL.Path
host = r.GetHost()
)
Go version
go version go1.26.1 linux/amd64
GoFrame version
v2.10.0
Can this bug be reproduced with the latest release?
Option Yes
What did you do?
Using a lowercase HTTP method (e.g., get) to send a request via http.NewRequest("get", ...) to a route that is registered with an exact method match (e.g., GET:/api/user):
What did you see happen?
The first request using a lowercase
getreturns404 Not Found.Subsequent normal
GETrequests to the same path will permanently return404, even when using other HTTP clients (such as curl or Postman).The issue can only be resolved by restarting the service.
What did you expect to see?
A lowercase get should be treated as equivalent to GET, correctly matching the route and returning 200 (HTTP method names are case-insensitive per RFC specifications).
Even if lowercase methods are not supported, they should not pollute the cache and cause subsequent valid requests to permanently return 404.
The best fix is to normalize the HTTP method at the entry point of getHandlersWithCache:
Change it to: