echo icon indicating copy to clipboard operation
echo copied to clipboard

Not able to detect the route url without \

Open PuneetAgrawal-plivo opened this issue 2 years ago • 9 comments

Issue Description

I had created routes using POST with path parameters. If the value is not passed in path params it is not able to read the route and if the value is passed it is able to read it

route("VerifiedCallerId/Verification/:verification_uuid", tc.ValidateOtp, POST),

if the :verification_uuid is not passed then getting 405 if the value is passed working properly

Checklist

  • [ yes] Dependencies installed
  • [ yes] No typos
  • [ yes] Searched existing issues and docs

Expected behaviour

it should go to the path function and process

Actual behaviour

it is returning method not allowed Status code : 405 { "message": "Method Not Allowed" }

Steps to reproduce

Working code to debug

package main

func main() {
}

Version/commit

PuneetAgrawal-plivo avatar Nov 16 '23 07:11 PuneetAgrawal-plivo

Please check your server logs if that request is actually sent as POST? Status code 405 hints that OPTIONS method is being used.

or maybe you could create working example. You can start from this:

func main() {
	e := echo.New()
	e.Use(middleware.Logger())

	e.POST("VerifiedCallerId/Verification/:verification_uuid", func(c echo.Context) error {
		return c.String(http.StatusOK, "OK: "+c.Param("verification_uuid"))
	})

	if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
		e.Logger.Fatal(err)
	}
}
curl -v -d "param1=value1&param2=value2" -X POST http://localhost:8080/VerifiedCallerId/Verification/my_uid

aldas avatar Nov 16 '23 08:11 aldas

I have checked the method is post. There is one more method below this which route("VerifiedCallerId/:phone_number", tc.GetVerifiedID, GET),

it is picking this route as Verification as :phone_number and then returning method not allowed.

Ideally it should pick the above route because of longest matching path but it is picking this one

PuneetAgrawal-plivo avatar Nov 16 '23 08:11 PuneetAgrawal-plivo

Please try to recreate that situation with that example and add appropriate curl so that I can test this out.

aldas avatar Nov 16 '23 08:11 aldas

func main() {
	e := echo.New()
	e.Use(middleware.Logger())

	e.POST("VerifiedCallerId/Verification/:verification_uuid", func(c echo.Context) error {
		return c.String(http.StatusOK, "OK")
	})
	e.GET("VerifiedCallerId/:phone_number", func(c echo.Context) error {
		return c.JSONPretty(http.StatusOK, map[string]interface{}{"message": "welcome to Plivo"}, " ")
	})

	if err := e.Start(":5000"); err != nil && !errors.Is(err, http.ErrServerClosed) {
		e.Logger.Fatal(err)
	}
}

You cun run this file go run recreate.go

and then hit this curl curl -v --request POST 'http://localhost:5000/VerifiedCallerId/Verification/'

you can reproduce the same

PuneetAgrawal-plivo avatar Nov 16 '23 08:11 PuneetAgrawal-plivo

ok, I see. Currently then the path param is last segment in path it does not match empty values. This is why you get 405 as it matches path for that GET route but method is different.

Currently workaround for this situation is to use * any route and get that value param value

	e.POST("VerifiedCallerId/Verification/*", func(c echo.Context) error {
		return c.String(http.StatusOK, "OK:"+c.Param("*"))
	})

aldas avatar Nov 16 '23 08:11 aldas

note to self: this line is probably the reason why empty path params at the end are not matched

https://github.com/labstack/echo/blob/584cb85a6b749846ac26a8cd151244ab281f2abc/router.go#L654

and same reason why empty any segment works is here https://github.com/labstack/echo/blob/584cb85a6b749846ac26a8cd151244ab281f2abc/router.go#L676

I need to understand why we are doing this and then we can decide if this can be changed and asses the implication of that change

aldas avatar Nov 16 '23 08:11 aldas

Thanks for the info

PuneetAgrawal-plivo avatar Nov 16 '23 09:11 PuneetAgrawal-plivo

@aldas how should the processing proceed in this scenario. Should it throw a 404 or should it let the endpoint process it if there is a "/"?

ProgrammingMuffin avatar Dec 17 '23 12:12 ProgrammingMuffin

One workaround would be to change from e.POST("a/b/:uid", h) to e.POST("a/b/*", h) and access c.Param("uid") with c.Param("*").

Path params (:uid) and match-any (*) work little bit differently when comes to the end of request url. I remember that path params allow empty values when it is in the middle of route path. For example Route with path e.GET("v1/:1/list/:2", h) matches with request /v1//list/test

note:

this may not be that simple. Consider this case

a)

	e.GET("/*", handlerFunc) // 1
	e.GET("/users/", handlerFunc) // 2
	e.GET("/users/:id", handlerFunc) // 3

b)

	e.GET("/*", handlerFunc) // 1
	e.GET("/users/:id", handlerFunc) // 2

when the request URL is /users/ what should be the correct match for a) and b)

aldas avatar Apr 06 '24 14:04 aldas