英文:
Adding to GET the same endpoint with POST and different Queries ends up with inconsistent error messages
问题
当使用不同的方法添加相同的路由时,每种方法的查询响应是不同的。然而,由于另一种方法是POST,所以它不应受到影响。
使用POST方法时:
Playground: https://go.dev/play/p/xzoAkpEhGgy
// 你可以编辑这段代码!
// 点击这里开始输入。
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// 一个示例API处理程序
fmt.Fprintf(w, "You made a POST request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).Methods("POST")
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// 一个示例API处理程序
fmt.Fprintf(w, "You made a GET request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).
Queries("from", "{from:[0-9]+}",
"to", "{to:[0-9]+}").Methods("GET")
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
go srv.ListenAndServe()
req2 := httptest.NewRequest("GET", "/api/v2?from=3&to=-5", nil)
out2 := httptest.NewRecorder()
r.ServeHTTP(out2, req2)
fmt.Println(out2.Code)
fmt.Println(out2)
}
预期结果为404,实际得到的是405。
移除POST方法后的结果:
Playground: https://go.dev/play/p/EXiF00_WrFW
// 你可以编辑这段代码!
// 点击这里开始输入。
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// 一个示例API处理程序
fmt.Fprintf(w, "You made a GET request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).
Queries("from", "{from:[0-9]+}",
"to", "{to:[0-9]+}").Methods("GET")
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
go srv.ListenAndServe()
req2 := httptest.NewRequest("GET", "/api/v2?from=3&to=-5", nil)
out2 := httptest.NewRecorder()
r.ServeHTTP(out2, req2)
fmt.Println(out2.Code)
}
结果为404。
对于GET请求,路由和结果应该是一致的,都是404错误。我想知道是否有人遇到过这个问题?
英文:
When adding the same routes with different methods, and per method queries the response of a get call is different, however since the other method is POST it should be unaffected.
With POST:
Playground: https://go.dev/play/p/xzoAkpEhGgy
// You can edit this code!
// Click here and start typing.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
fmt.Fprintf(w, "You made a POST request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).Methods("POST")
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
fmt.Fprintf(w, "You made a GET request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).
Queries("from", "{from:[0-9]+}",
"to", "{to:[0-9]+}").Methods("GET")
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
go srv.ListenAndServe()
req2 := httptest.NewRequest("GET", "/api/v2?from=3&to=-5", nil)
out2 := httptest.NewRecorder()
r.ServeHTTP(out2, req2)
fmt.Println(out2.Code)
fmt.Println(out2)
}
Expected 404, got 405
Whilst removing POST
playground: https://go.dev/play/p/EXiF00_WrFW
// You can edit this code!
// Click here and start typing.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
fmt.Fprintf(w, "You made a GET request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).
Queries("from", "{from:[0-9]+}",
"to", "{to:[0-9]+}").Methods("GET")
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
go srv.ListenAndServe()
req2 := httptest.NewRequest("GET", "/api/v2?from=3&to=-5", nil)
out2 := httptest.NewRecorder()
r.ServeHTTP(out2, req2)
fmt.Println(out2.Code)
}
Results in 404
For GET request the routing and result should be consistent. 404-s
I am curious if anyone seen this problem before?
答案1
得分: 1
路由器将根据路径和查询参数尝试找到匹配项。GET路由不匹配,因为查询字符串参数不符合要求。
但是,路径匹配了POST路由,因为该路由不关心这些查询参数。然后检查请求方法,它与路由不匹配,因此返回405 Method Not Allowed
(该路由有一个处理程序,但是用于不同的方法)。
您可以通过为相同路径添加一个捕获所有GET处理程序来获得所需的行为:
// You can edit this code!
// Click here and start typing.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
fmt.Fprintf(w, "You made a POST request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).Methods("POST")
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
fmt.Fprintf(w, "You made a GET request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).
Queries("from", "{from:[0-9]+}",
"to", "{to:[0-9]+}").
Methods("GET")
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "", http.StatusNotFound)
}).Methods("GET")
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
go srv.ListenAndServe()
req2 := httptest.NewRequest("GET", "/api/v2?from=3&to=-5", nil)
out2 := httptest.NewRecorder()
r.ServeHTTP(out2, req2)
fmt.Println(out2.Code)
}
英文:
Router will try to find a match based on path and query parameters. The GET route is not matched because of query string parameters not matching the requirements.
However the path matches the POST route because that route doesn't care about those query parameters. Then the request method is checked and it doesn't match the route so 405 Method Not Allowed
is returned (there is a handler for that route, but for different method).
You can get the desired behavior by adding catch-all GET handler for the same path:
// You can edit this code!
// Click here and start typing.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
fmt.Fprintf(w, "You made a POST request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).Methods("POST")
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
fmt.Fprintf(w, "You made a GET request")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}).
Queries("from", "{from:[0-9]+}",
"to", "{to:[0-9]+}").
Methods("GET")
r.HandleFunc("/api/v2", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "", http.StatusNotFound)
}).Methods("GET")
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
go srv.ListenAndServe()
req2 := httptest.NewRequest("GET", "/api/v2?from=3&to=-5", nil)
out2 := httptest.NewRecorder()
r.ServeHTTP(out2, req2)
fmt.Println(out2.Code)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论