Go的HTTP服务器如何处理POST数据的差异?

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

Go http server handle POST data difference?

问题

我有一个简单的上传表单,如下所示:

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

服务器端代码如下:

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)
}

问题是当我使用 enctype="multipart/form-data" 时,我无法通过 r.PostFormValue 从客户端获取值,但如果我将其设置为 enctype="application/x-www-form-urlencoded",Go文档中说:

PostFormValue返回POST或PUT请求体中命名组件的第一个值。URL查询参数将被忽略。
如果需要,PostFormValue会调用ParseMultipartForm和ParseForm,并忽略这些函数返回的任何错误。

那么为什么他们在这里没有提到 enctype 呢?

英文:

I have a simple upload form as

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

And server side

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

The problem is when I use enctype=&quot;multipart/form-data&quot;, I cannot get the value from client with r.PostFormValue, but it's ok if I set to enctype=&quot;application/x-www-form-urlencoded&quot;, 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 ?

答案1

得分: 1

如果你使用"multiplart/form-data"作为表单数据的编码类型,你需要使用Request.FormValue()函数来读取表单的值(注意不是PostFormValue)。

将你的defaultHandler()函数修改为:

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

这样就可以正常工作了。原因是因为Request.FormValue()Request.PostFormValue()在需要时(如果表单编码类型是multipart且尚未解析)都会首先调用Request.ParseMultipartForm(),而Request.ParseMultipartForm()只会将解析后的表单键值对存储在Request.Form中,而不是Request.PostForm中:Request.ParseMultipartForm()源代码

这可能是一个bug,但即使这是预期的工作方式,文档中也应该提到。

英文:

If you use &quot;multiplart/form-data&quot; 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(&quot;filepath&quot;))
}

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.

答案2

得分: 0

如果您想上传文件,您需要使用 multipart/form-data 编码类型,输入字段必须是 type=file,并使用 FormFile 方法而不是 PostFormValue 方法(后者只返回一个字符串)。

<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)

	// 将文件复制到文件夹或其他位置
}

func main() {
	http.HandleFunc("/up", defaultHandler)
	http.ListenAndServe(":8899", nil)
}
英文:

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.

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

<!-- -->

package main

import (
	&quot;log&quot;
	&quot;net/http&quot;
)

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

	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(&quot;/up&quot;, defaultHandler)
	http.ListenAndServe(&quot;:8899&quot;, nil)
}

huangapple
  • 本文由 发表于 2015年1月20日 18:17:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/28042789.html
匿名

发表评论

匿名网友

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

确定