secmask
secmask

Reputation: 8107

Go http server handle POST data difference?

I have a simple upload form as

<html>
<title>Go upload</title>
<body>
<form action="http://localhost:8899/up" method="post" enctype="multipart/form-data">
<label for="file">File Path:</label>
<input type="text" name="filepath" id="filepath">
<p>
<label for="file">Content:</label>
<textarea name="jscontent" id="jscontent" style="width:500px;height:100px" rows="10" cols="80"></textarea>
<p>
<input type="submit" name="submit" value="Submit">
</form>
</body>
</html>

And server side

package main 
import (
    "net/http"
    "log"
)
func defaultHandler(w http.ResponseWriter, r *http.Request) {
    log.Println(r.PostFormValue("filepath"))
}
func main() {
    http.HandleFunc("/up", defaultHandler)
    http.ListenAndServe(":8899", nil)
}

The problem is when I use enctype="multipart/form-data", I cannot get the value from client with r.PostFormValue, but it's ok if I set to enctype="application/x-www-form-urlencoded", go document say

PostFormValue returns the first value for the named component of the POST or PUT request body. URL query parameters are ignored. PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores any errors returned by these functions.

So why they did not say anything about enctype here ?

Upvotes: 1

Views: 1685

Answers (2)

icza
icza

Reputation: 417592

If you use "multiplart/form-data" form-data encoding type you have to read form values with the Request.FormValue() function (note that not PostFormValue!).

Change your defaultHandler() function to:

func defaultHandler(w http.ResponseWriter, r *http.Request) {
    log.Println(r.FormValue("filepath"))
}

And it will work. The reason for this is because both Request.FormValue() and Request.PostFormValue() first call Request.ParseMultipartForm() if needed (if form encodying type is multipart and it has not yet been parsed) and Request.ParseMultipartForm() only stores the parsed form name-value pairs in the Request.Form and not in Request.PostForm: Request.ParseMultipartForm() source code

This may well be a bug, but even if this is the intended working, it should be mentioned in the documentation.

Upvotes: 1

If you are trying to upload files you need to use the multipart/form-dataenctype, the input field must be type=file and use the FormFile instead of PostFormValue (that returns just a String) method.

<html>
<title>Go upload</title>
<body>
    <form action="http://localhost:8899/up" method="post" enctype="multipart/form-data">
        <label for="filepath">File Path:</label>
        <input type="file" name="filepath" id="filepath">
        <p>
        <label for="jscontent">Content:</label>
        <textarea name="jscontent" id="jscontent" style="width:500px;height:100px" rows="10" cols="80"></textarea>
        <p>
        <input type="submit" name="submit" value="Submit">
    </form>
</body>
</html>
package main

import (
    "log"
    "net/http"
)

func defaultHandler(w http.ResponseWriter, r *http.Request) {
    file, header, err := r.FormFile("filepath")

    defer file.Close()

    if err != nil {
        log.Println(err.Error())
    }

    log.Println(header.Filename)

    // Copy file to a folder or something
}
func main() {
    http.HandleFunc("/up", defaultHandler)
    http.ListenAndServe(":8899", nil)
}

Upvotes: 0

Related Questions