Adding to GET the same endpoint with POST and different Queries ends up with inconsistent error messages

huangapple go评论85阅读模式
英文:

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)

}

huangapple
  • 本文由 发表于 2022年12月7日 22:33:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/74718292.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定