HTTP POST提交数据方式不同引起的躺坑记

HTTP POST提交数据方式不同引起的躺坑记

jasonye 2017-08-12

问题现象:

PHP代码中调用curl POST提交的数据,在Go Server端一直用PostFormValue读不到,但是用curl -d命令提交的数据可以读取到。

定位问题:

在试图采用postman手动post数据到后端看能否重现问题的过程中,无意发现Postman中提供了四种POST提交方式:

  1. application/x-www-form-urlencoded
  2. multipart/form-data
  3. raw
  4. binary

进一步试验发现,不同提交方式到后台结果不一样。采用 application/x-www-form-urlencoded 可以读取到,而 multipart/form-data 读取不到。

进一步发现:这两种提交方式的不同,在数据传输的形式上也是不同的。分别如下:

application/x-www-form-urlencoded如下:

POST /CrowdCal HTTP/1.1
Host: x.x.x.x:xxxx
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: 76d762fa-c06a-6f0f-168f-cabac25f5d11

Fcal_formula=1&Fcal_job_id=206&Fcustom_id=1

而 multipart/form-data:

POST /CrowdCal HTTP/1.1
Host: x.x.x.x:xxxx
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: fa780fa5-ab1e-97bc-0e2b-91e40483412a

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="Fcal_formula"

AA=1&BB=1^C=1
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="Fcal_job_id"

12
------WebKitFormBoundary7MA4YWxkTrZu0gW

于是问题差不多定位到了,是由于前后端PHP代码和GO代码对POST提交数据的解析方式不统一引起的问题。

解决问题:

方法一:
原来PHP代码使用如下方式提交:

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $arrData);

进一步查看PHP文档解析如下:
CURLOPT_POST : TRUE to do a regular HTTP POST. This POST is the normal application/x-www-form-urlencoded kind, most commonly used by HTML forms.

CURLOPT_POSTFIELDS 提交的第三个参数:
If value is an array, the Content-Type header will be set to multipart/form-data.

并且特别Note:
Passing an array to CURLOPT_POSTFIELDS will encode the data as multipart/form-data, while passing a URL-encoded string will encode the data as application/x-www-form-urlencoded.

于是上面代码修改为这样解决:

$strQuery = http_build_query($arrData);
curl_setopt($ch, CURLOPT_POST, true);
// curl_setopt($ch, CURLOPT_POSTFIELDS, $arrData);
curl_setopt($ch, CURLOPT_POSTFIELDS, $strQuery);

问题解决,改成使用application/x-www-form-urlencoded方式数据,后端就可以正确解析提交的数据了。

方法二:
上面的问题也可以通过GO Server端改变解析POST数据的方式来解决。

Go端原来代码如下:

    r.ParseForm()
    //r.ParseMultipartForm(100)

    fmt.Println("PostFormValue", r.PostFormValue("Fcal_formula"))

查看r.ParseForm文档:
For all requests, ParseForm parses the raw query from the URL and updates r.Form.

For POST, PUT, and PATCH requests, it also parses the request body as a form and puts the results into both r.PostForm and r.Form. Request body parameters take precedence over URL query string values in r.Form.

For other HTTP methods, or when the Content-Type is not application/x-www-form-urlencoded, the request Body is not read, and r.PostForm is initialized to a non-nil, empty value.

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. If key is not present, PostFormValue returns the empty string.
**

于是修改去掉 r.ParseForm 就好了。

进一步说明:

上述4中方式,其实是POST的:Form content types 参考[https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4]

HTTP协议默认要求:
User agents must support the content types listed below. Behavior for other content types is unspecified.
application/x-www-form-urlencoded 和 multipart/form-data

其他的的类型并没有要求,但是越来越多的浏览器、WEB站点开始支持
raw类型:
包括:text/plain, appliation/json , appliation/javascript , application/xml , application/html ,application/xml, text/xml 等


binary:即发送二进制文件

POST /pcc HTTP/1.1
Host: 127.0.0.1:8808
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: c30910c3-8371-a472-4b1a-6640e85f4623

{"sfj":{"sfd":"sdff"}}

参考文献:
https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
https://imququ.com/post/four-ways-to-post-data-in-http.html
Go文档、
PHP文档

你可能感兴趣的:(web,php)