客户端发送数据及服务器获取参数的几种形式
查询字符串传递及用$_GET获取
在前端,将参数拼接在查询字符串上,如 www.wubin.work/xx.php?id=10¶m=ok
在服务端,使用$_GET获取
$id = $_GET['id'];
$param = $_GET['param'];
HTTP请求头
enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码,默认地,表单数据会编码为 “application/x-www-form-urlencoded”。就是说,在发送到服务器之前,所有字符都会进行编码(空格转换为 “+” 加号,特殊符号转换为 ASCII HEX 值)。
enctype可选项如下:
值 | 描述 |
---|---|
application/x-www-form-urlencoded | 在发送前编码所有字符(默认) |
multipart/form-data | 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。 |
application/json | 作为请求头告诉服务端消息主体是序列化的JSON字符串。除低版本的IE,基本都支持。 |
text/plain | 空格转换为 “+” 加号,但不对特殊字符编码。 |
HTTP 协议是以ASCII
码传输。他是建立在 TCP/IP 协议之上的应用层规范, 规定HTTP 请求分为三个部分:
- 状态行
- headers请求头
- entity-body 消息主体
HTTP协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。但是,数据发送出去,还要服务端解析成功才有意义。一般服务端语言如 php、python 等,以及它们的 framework,都内置了自动解析常见数据格式的功能。服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以 POST 提交数据包含了 Content-Type 和消息主体编码方式两部分,下面就正式开始介绍它们。
三种常见的POST提交方式
以下例子均以JQuery为例。以下是基本的数据结构:
var data_obj = {
one: 'first',
two: 'second'
};
// 将对象转化为字符串
var data_str = JSON.stringify(data_obj);
application/x-www-form-urlencoded
HTTP中默认的提交数据的方式。提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。(JQ不传递contentType默认就是x-www-form-urlencoded)如:
$.ajax({
url: 'post-json.php',
type: 'POST',
data: data_obj
...
});
在chrome的network-formData可以看到 会拼接成one=first&two=second。传入json对象的时候,这种方式会以键值对的形式将对象序列化,所以传进去的对象实际上还是变成了字符流。
这里特别注意一点:这里的data不可以传入data: data_str(字符串)! (以下对比3.3 application/json进行区分!)比如
$.ajax({
url: 'post-json.php',
type: 'POST',
data: data_str
});
post-json.php:
var_dump($_POST);
exit();
最终的结果是: array(1) { ["{"one":"first","two":"second"}"]=> string(0) "" },传入的JSON字符串成为了键名!
如果要传入字符串,那么一定是要传入一个对象:如
$.ajax({
url: 'post-json.php',
type: 'POST',
data: {str: data_str},
...
});
最终的结果是:array(1) { ["str"]=> string(30) "{"one":"first","two":"second"}" } 可以通过$_POST['str']获取。
multipart/form-data
一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须将 的enctype
设为multipart/form-data
。
content-type: multipart/form-data; boundary=--wxx
// form表单的数据,以boundary的字符串进行分割各个请求参数,结果就是
--wxx Content-Disposition: form-data;name="one"
first
--wxx Content-Disposition: form-data;name="two"
second
/* 以上在chrome的控制台 network-xhr-formData中可看到 */
application/json
application/json
作为请求头,用来告诉服务端消息主体是序列化的JSON字符串,除了低版本的IE,基本都支持。
注意这里发送的数据必须是JSON字符串!!!
$.ajax({
url: 'post-json.php',
type: 'POST',
contentType: 'application/json',
data: data_str, // 必须是字符串!
// 当contentType: 'application/json'时候,传入的是对象data_obj,那么后台不会接收到数据
success: function() {
console.log('ok');
}
});
在jquery的ajax中,如果没加contentType:"application/json",那么data就应该对应的是json对象;
如果加了contentType:"application/json",那么ajax发送的就必须是(json)字符串。
post-json.php(重点):
// php://input 是个可以访问请求的原始数据的只读流
// enctype="multipart/form-data" 的时候 php://input 是无效的。
$re = file_get_contents("php://input");
// 将获取的json字符串转化为数组
$reArr = json_decode($re, true);
print_r($reArr);
$one = $reArr['one'];
$two = $reArr['two'];
echo $one . ' ' . $two;
注意,服务端获取application/json请求头,必须要使用file_get_contents("php://input"); 使用$_POST无法获取application/json请求头的数据。
最终结果:(print_r的->)Array ( [one] => first [two] => second ) (echo的->)first second
在vue中使用axios提交数据
// 公共方法的代码
import axios from "axios";
import { POST_IMG_URL } from "api/config";
function postUrl(url, paramObj) {
return axios.post(url, paramObj).then((res) => {
return Promise.resolve(res.data);
}).catch((err) => {
return Promise.reject(err);
});
}
服务端使用$_POST进行接收
header("Access-Control-Allow-Origin: *");
header("content:application/json;chartset=uft-8");
$zt_id = isset($_POST['id']) ? intval($_POST['id']) : false;
$url = isset($_POST['url']) ? $_POST['url'] : false;
当数据使用json对象提交
export function postImgUrl(ztId, url) {
let obj = {
id: ztId,
url: url
};
return postUrl(POST_IMG_URL, obj);
}
这时得到的,请求头是application/json 请求发送失败。因为$_POST无法接收JSON对象。
使用拼接字符串
export function postImgUrl(ztId, url) {
let str = `id=${ztId}&url=${url}`;
return postUrl(POST_IMG_URL, str);
}
这次将获取到的参数使用&符号拼接,结果发现发送成功了。
上图是点击view source 是将参数使用&符号链接了。
由此可见,axios是根据第二个参数提交的格式,来自动设置content-type的。如果要使用json格式的请求,请参见上文介绍的使用php://input方式获取。
axios可以自定义请求头
// 实现axios异步上传图片
// 设置请求头为multipart/form-data
let post_config = {
headers: {'Content-Type': 'multipart/form-data'}
};
function uploadImg(param) {
return axios.post(地址, 发送的参数, post_config).then((res) => {
return Promise.resolve(res.data);
}).catch((err) => {
return Promise.reject(err);
});
}
// 请求头设置为application/x-www-form-urlencoded
let post_config = {
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
};