Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PUT does not work. Always 404 #450

Closed
vinceyuan opened this issue Oct 2, 2015 · 11 comments
Closed

PUT does not work. Always 404 #450

vinceyuan opened this issue Oct 2, 2015 · 11 comments

Comments

@vinceyuan
Copy link
Contributor

I defined router.PUT("/admin/sourcesentences/:id/update", routes.UpdateSourceSentence)

When the web app runs, it prints [GIN-debug] PUT /admin/sourcesentences/:id/update --> myproject/routes.UpdateSourceSentence (4 handlers).

But when I really send a PUT request via http form (I override POST), I always get 404 [GIN] 2015/10/02 - 23:03:36 | 404 | 89.501µs | [::1]:56720 | PUT /admin/sourcesentences/40/update

At last, I have to use POST.

@javierprovecho
Copy link
Member

I'll take a look at it.

NOTE: The information in this e-mail is confidential, being solely for the addressee named above. If you read this message and not the intended recipient, please note that it is completely forbidden any use, disclosure, distribution and / or duplication of this communication without the consent of the sender. If you have received this message in error, please notify us immediately by electronic mail and its elimination.

On Fri, Oct 2, 2015 at 6:16 PM, Vince Yuan notifications@github.com
wrote:

I defined router.PUT("/admin/sourcesentences/:id/update", routes.UpdateSourceSentence)
When the web app runs, it prints [GIN-debug] PUT /admin/sourcesentences/:id/update --> myproject/routes.UpdateSourceSentence (4 handlers).
But when I really send a PUT request via http form (I override POST), I always get 404 [GIN] 2015/10/02 - 23:03:36 | 404 | 89.501µs | [::1]:56720 | PUT /admin/sourcesentences/40/update

At last, I have to use POST.

Reply to this email directly or view it on GitHub:
#450

@vinceyuan
Copy link
Contributor Author

@javierprovecho Thanks.

I override http form method using martini-contrib/method. I am not sure this way is correct. I searched but did not find an example of overriding method for gin. (I am using http form and add something like this <input type="hidden" name="_method" value="PUT"> into <form> tag)

import "github.com/martini-contrib/method"

var overrideHandler = method.Override()
func Override(c *gin.Context) {
    overrideHandler.ServeHTTP(c.Writer, c.Request)
}

func main() {
    router := gin.Default()
    router.Use(Override)

DELETE has the same problem too. If I use router.POST for a DELETE form request, gin recognizes the request as a DELETE but handled it with a POST handler without errors. If I use router.DELETE for a DELETE form request, it says 404.

@javierprovecho javierprovecho added bug and removed bug labels Oct 5, 2015
@javierprovecho
Copy link
Member

@vinceyuan hey, this bad behaviour is because how the router works, not because PUT or DELETE doesn't work. Please avoid using parameters between static path. We will try to make this more friendly, but understand this is for speed and performance.

EDIT: I tried both PUT and DELETE with a standard path in develop and master, and it didn't gave me any problem.

@vinceyuan
Copy link
Contributor Author

@jasonrhansen Thanks for the response. I made a simple app to reproduce this problem. You can copy into a file and run it. There are just 2 routes GET / and PUT /:id. GET / shows a form. Press 'PUT' button to send a PUT request (overriding POST). Still gets 404 for PUT /:id. I am using martini-contrib/method for overriding form method. Not sure my Override function is correct.

screen shot 2015-10-06 at 12 24 13 am

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/martini-contrib/method"
    "html/template"
    "net/http"
)

var overrideHandler = method.Override()
func Override(c *gin.Context) {
    overrideHandler.ServeHTTP(c.Writer, c.Request)
}

func main() {
    router := gin.Default()
    router.Use(Override)
    html := template.Must(template.New("").Parse(`<html><body><h1>Hello</h1><form action="/1" method="post"><input type="hidden" name="_method" value="PUT"><input type="submit" value="PUT"></form></body></html>`))
    router.SetHTMLTemplate(html)
    router.GET("/", func(c *gin.Context) {
        c.HTML(http.StatusOK, "", nil)
    })
    router.PUT("/:id", func(c *gin.Context) {
        fmt.Println("Received PUT request")
        c.Redirect(http.StatusSeeOther, "/")
    })
    router.Run(":9200")
}

You can see PUT /:id is accepted, but does not really work. Output:

$ ./verifybug
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET   /                         --> main.main.func1 (4 handlers)
[GIN-debug] PUT   /:id                      --> main.main.func2 (4 handlers)
[GIN-debug] Listening and serving HTTP on :9200
[GIN] 2015/10/06 - 00:01:30 | 200 |      194.09µs | [::1]:59433 |   GET     /
[GIN] 2015/10/06 - 00:01:32 | 404 |      56.962µs | [::1]:59433 |   PUT     /1

If I change the code router.PUT("/:id", func(c *gin.Context) { to router.PUT("/1", func(c *gin.Context) {, I get the same result:

$ ./verifybug
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET   /                         --> main.main.func1 (4 handlers)
[GIN-debug] PUT   /1                        --> main.main.func2 (4 handlers)
[GIN-debug] Listening and serving HTTP on :9200
[GIN] 2015/10/06 - 00:16:48 | 200 |       201.4µs | [::1]:59530 |   GET     /
[GIN] 2015/10/06 - 00:16:50 | 404 |      41.151µs | [::1]:59530 |   PUT     /1

As I mentioned above, I am not sure my way of overriding method is correct. If it is wrong, could you please show me how to do it correctly? It's not urgent for me, because I can just use POST. But I think somebody else may really need PUT/DELETE.

Thanks!

@nazwa
Copy link

nazwa commented Oct 7, 2015

Hey @vinceyuan
I just run your code locally and it seems to work totally fine (but with a catch!).

Demo

The requests are handled correctly when sent through postman or any other api testing tool. I'm sure curl would also give you success on both, get and put.
The problem lies in browser - chrome (not sure about others) doesn't seem to submit PUT requests through forms. If you inspect the outgoing requests, even when method is set to PUT, the submission goes out as GET.

So yes, your code is fine, just the way you use it is not 😄 Send the form from JS and I'm sure it will work.

@javierprovecho
Copy link
Member

@nazwa, probably html forms in Chrome don't allow to do this. If you (@vinceyuan) need to send a PUT request, I suggest using a javascript framework such as angularjs, to archive maximum compatibility between browsers. of course it depends on the requisites of your application.

NOTE: The information in this e-mail is confidential, being solely for the addressee named above. If you read this message and not the intended recipient, please note that it is completely forbidden any use, disclosure, distribution and / or duplication of this communication without the consent of the sender. If you have received this message in error, please notify us immediately by electronic mail and its elimination.

On Wed, Oct 7, 2015 at 8:33 PM, nazwa notifications@github.com wrote:

Hey @vinceyuan
I just run your code locally and it seems to work totally fine (but with a catch!).
Demo
The requests are handled correctly when sent through postman or any other api testing tool. I'm sure curl would also give you success on both, get and put.
The problem lies in browser - chrome (not sure about others) doesn't seem to submit PUT requests through forms. If you inspect the outgoing requests, even when method is set to PUT, the submission goes out as GET.

So yes, your code is fine, just the way you use it is not 😄

Reply to this email directly or view it on GitHub:
#450 (comment)

@javierprovecho
Copy link
Member

thank you @nazwa by the way for your research! 

NOTE: The information in this e-mail is confidential, being solely for the addressee named above. If you read this message and not the intended recipient, please note that it is completely forbidden any use, disclosure, distribution and / or duplication of this communication without the consent of the sender. If you have received this message in error, please notify us immediately by electronic mail and its elimination.

On Wed, Oct 7, 2015 at 8:33 PM, nazwa notifications@github.com wrote:

Hey @vinceyuan
I just run your code locally and it seems to work totally fine (but with a catch!).
Demo
The requests are handled correctly when sent through postman or any other api testing tool. I'm sure curl would also give you success on both, get and put.
The problem lies in browser - chrome (not sure about others) doesn't seem to submit PUT requests through forms. If you inspect the outgoing requests, even when method is set to PUT, the submission goes out as GET.

So yes, your code is fine, just the way you use it is not 😄

Reply to this email directly or view it on GitHub:
#450 (comment)

@nazwa
Copy link

nazwa commented Oct 7, 2015

@javierprovecho no problem! We use gin for everything, so this is the least I can do in return 😄

@vinceyuan
Copy link
Contributor Author

@nazwa @javierprovecho None of browsers support PUT in form. That's why we add a hidden input <input type="hidden" name="_method" value="PUT"> in the form. See ExpressJs CURD here

<form action="/1" method="post">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="PUT">
</form>

When the server receives a POST request and the form data contains "_method=PUT", it should treat it as a PUT request. We call it overriding. I am using martini-contrib/method to override post method. I searched but don't find an example of overriding post method in gin.

@nazwa
Copy link

nazwa commented Oct 8, 2015

Och haha, ok. To me this sounds like an art for the sake of art but what do I know. And i'm almost sure it won't work with gin, as the route is determined before middleware, so if requests comes in as post, it is treated as post.

Workaround? Use the 'No method' handler and trigger the appropriate route handler manually, but again, not sure what's the value here.

@javierprovecho
Copy link
Member

@vinceyuan I suggest using r.Any() for this. Due to the architecture of Gin, overriding is not possible by the middlewares. Closing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants