Description
How to change the http request method?
for example: https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/#_method-or-x-http-method-override-header
A POST to /wp-json/wp/v2/posts/42?_method=DELETE
would be translated to a DELETE to the wp/v2/posts/42
route.
How to reproduce
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"snowdream.tech/press-backend/pkg/i18n"
ghttp "snowdream.tech/press-backend/pkg/net/http"
)
func loadMethodAPI(router *gin.Engine) {
router.GET(Namespace, func(c *gin.Context) {
i18 := i18n.Default(c)
data := gin.H{
"message": i18.T(c, "Hello Snowdream Server! GET"),
}
ghttp.NegotiateResponse(c, http.StatusOK, ghttp.ResponseSuccessWithData(c, data))
})
router.POST(Namespace, func(c *gin.Context) {
i18 := i18n.Default(c)
data := gin.H{
"message": i18.T(c, "Hello Snowdream Server! POST"),
}
ghttp.NegotiateResponse(c, http.StatusOK, ghttp.ResponseSuccessWithData(c, data))
})
router.PUT(Namespace, func(c *gin.Context) {
i18 := i18n.Default(c)
data := gin.H{
"message": i18.T(c, "Hello Snowdream Server! PUT"),
}
ghttp.NegotiateResponse(c, http.StatusOK, ghttp.ResponseSuccessWithData(c, data))
})
router.DELETE(Namespace, func(c *gin.Context) {
i18 := i18n.Default(c)
data := gin.H{
"message": i18.T(c, "Hello Snowdream Server! DELETE"),
}
ghttp.NegotiateResponse(c, http.StatusOK, ghttp.ResponseSuccessWithData(c, data))
})
router.PATCH(Namespace, func(c *gin.Context) {
i18 := i18n.Default(c)
data := gin.H{
"message": i18.T(c, "Hello Snowdream Server! PATCH"),
}
ghttp.NegotiateResponse(c, http.StatusOK, ghttp.ResponseSuccessWithData(c, data))
})
router.HEAD(Namespace, func(c *gin.Context) {
i18 := i18n.Default(c)
data := gin.H{
"message": i18.T(c, "Hello Snowdream Server! HEAD"),
}
ghttp.NegotiateResponse(c, http.StatusOK, ghttp.ResponseSuccessWithData(c, data))
})
router.OPTIONS(Namespace, func(c *gin.Context) {
i18 := i18n.Default(c)
data := gin.H{
"message": i18.T(c, "Hello Snowdream Server! OPTIONS"),
}
ghttp.NegotiateResponse(c, http.StatusOK, ghttp.ResponseSuccessWithData(c, data))
})
}
package middlewares
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
"snowdream.tech/press-backend/pkg/tools"
)
// Method Method
func Method() gin.HandlerFunc {
tools.DebugPrintF("[INFO] Starting Middleware %s", "Method")
return func(c *gin.Context) {
if _method, ok := c.GetQuery("_method"); ok && strings.ToUpper(_method) != c.Request.Method {
c.Request.Method = strings.ToUpper(_method)
c.Redirect(http.StatusSeeOther, c.Request.URL.Path)
c.Abort()
} else if _header := c.GetHeader("X-HTTP-Method-Override"); _header != "" && strings.ToUpper(_header) != c.Request.Method {
c.Request.Method = strings.ToUpper(_header)
c.Redirect(http.StatusSeeOther, c.Request.URL.Path)
c.Abort()
} else {
c.Next()
}
}
}
http://localhost:8080/wp/v2?_method=put
Expectations
change the http request method
Actual result
not work
Environment
- go version:
- gin version (or commit ref):
- operating system: macOS 12.7.2 go env GO111MODULE='on' GOARCH='amd64' GOBIN='' GOCACHE='/Users/snowdream/Library/Caches/go-build' GOENV='/Users/snowdream/Library/Application Support/go/env' GOEXE='' GOEXPERIMENT='' GOFLAGS='' GOHOSTARCH='amd64' GOHOSTOS='darwin' GOINSECURE='' GOMODCACHE='/Users/snowdream/go/pkg/mod' GONOPROXY='' GONOSUMDB='' GOOS='darwin' GOPATH='/Users/snowdream/go' GOPRIVATE='' GOPROXY='https://goproxy.cn' GOROOT='/usr/local/go' GOSUMDB='sum.golang.org' GOTMPDIR='' GOTOOLCHAIN='auto' GOTOOLDIR='/usr/local/go/pkg/tool/darwin_amd64' GOVCS='' GOVERSION='go1.21.4' GCCGO='gccgo' GOAMD64='v1' AR='ar' CC='clang' CXX='clang++' CGO_ENABLED='1' GOMOD='/dev/null' GOWORK='' CGO_CFLAGS='-O2 -g' CGO_CPPFLAGS='' CGO_CXXFLAGS='-O2 -g' CGO_FFLAGS='-O2 -g' CGO_LDFLAGS='-O2 -g' PKG_CONFIG='pkg-config' GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/pn/sl_vbqyx74jf365f5cgdbyv40000gn/T/go-build1723272133=/tmp/go-build -gno-record-gcc-switches -fno-common'
Comment From: snowdream
c.Request.Method = strings.ToUpper(_method)
engine.HandleContext(c)
c.Abort()
Comment From: RedCrazyGhost
When you use HTTP methods, consider the security aspects. For tighter security, some firewalls do not allow HTTP PUT or DELETE traffic.
f you're using a server that can't handle PUT or DELETE traffic, you should use the query or head argument logic in func(c *gin.Context).
The following code has been uploaded to the code repository
// HandleContext re-enters a context that has been rewritten.
// This can be done by setting c.Request.URL.Path to your new target.
// Disclaimer: You can loop yourself to deal with this, use wisely.
func (engine *Engine) HandleContext(c *Context) {
oldIndexValue := c.index
c.reset()
engine.handleHTTPRequest(c)
c.index = oldIndexValue
}
package main
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
func Method(r *gin.Engine) gin.HandlerFunc {
return func(c *gin.Context) {
if _method, ok := c.GetQuery("_method"); ok && strings.ToUpper(_method) != c.Request.Method {
c.Request.Method = strings.ToUpper(_method)
r.HandleContext(c)
c.Abort()
} else if _header := c.GetHeader("X-HTTP-Method-Override"); _header != "" && strings.ToUpper(_header) != c.Request.Method {
c.Request.Method = strings.ToUpper(_header)
r.HandleContext(c)
c.Abort()
}else{
c.Next()
}
}
}
func main() {
r := gin.Default()
r.Use(Method(r))
r.GET("/test", func(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{"message":"GET"})
})
r.POST("/test", func(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{"message":"POST"})
})
r.PUT("/test", func(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{"message":"PUT"})
})
if err := r.Run(":8080"); err != nil {
panic(err)
}
}