PHP 请求处理与 JSON 字段存储常见问题
PHP 中处理
application/x-www-form-urlencoded与application/json请求对比 , json字段的处理
在 PHP 开发中,处理 HTTP 请求时经常会遇到两种常见的 Content-Type:
application/x-www-form-urlencodedapplication/json; charset=utf-8
本文从 发送请求 和 接收请求 两个角度,结合具体代码示例,说明它们的区别与正确用法。
一、发送请求(客户端视角)
1. 发送 JSON 格式(推荐用于 API)
适用于调用开放平台接口(如饿了么、微信、阿里云等)。
$url = "https://your-api.com/endpoint";
$headers = [
"Content-Type: application/json; charset=utf-8"
];
$data = [
"nop" => "1.0.0",
"id" => "req_" . time(),
"metas" => [
"app_key" => "your_app_key",
"timestamp" => time()
],
"action" => "eleme.user.getUser",
"token" => "access_token_123",
"params" => [],
"signature" => "calculated_signature"
];
// 需要将data参数进行序列化
// JSON_UNESCAPED_UNICODE 保留原始 Unicode 字符(如中文直接输出),不进行 \u 转义:
$body = json_encode($data, JSON_UNESCAPED_UNICODE);
$response = httpPost($headers, $url, $body);✅ 支持嵌套对象、数组
✅ 是现代 RESTful API 的标准格式
2. 发送表单格式(传统 Web 表单)
适用于 HTML 表单提交或简单键值对场景。
$url = "https://your-api.com/endpoint";
$headers = [
"Content-Type: application/x-www-form-urlencoded"
];
// 注意:复杂结构需扁平化
$data = [
"action" => "eleme.user.getUser",
"token" => "access_token_123",
"app_key" => "your_app_key",
"timestamp" => time(),
// params 无法直接传数组,可转为 JSON 字符串
"params" => json_encode([]),
"signature" => "calculated_signature"
];
$body = http_build_query($data); // 自动 URL 编码
$response = httpPost($headers, $url, $body);⚠️ 不支持原生嵌套对象
⚠️ 开放平台 API 通常 不支持 此格
二、接收请求(服务端视角)
1. 接收 JSON 请求
PHP 不会自动解析 JSON 请求体,需手动读取 php://input。
header('Content-Type: application/json; charset=utf-8');
// 有些框架进行封装,省略
$input = file_get_contents('php://input');
// 进行反序列化
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON']);
exit;
}
// 使用数据
echo "Action: " . ($data['action'] ?? 'N/A') . "\n";
echo json_encode(['status' => 'success', 'user_id' => 12345]);✅ 必须使用 file_get_contents('php://input')
✅ $_POST 在此场景下为空
2. 接收 x-www-form-urlencoded 请求
PHP 自动解析请求体,并填充到 $_POST。
header('Content-Type: application/json; charset=utf-8');
// 直接使用 $_POST
$action = $_POST['action'] ?? null;
$token = $_POST['token'] ?? null;
//处理前端传 JSON 参数的方式
$params = json_decode($_POST['params'] ?? '[]', true); // 若 params 是 JSON 字符串
echo json_encode([
'received_action' => $action,
'params_count' => count($params)
]);✅ 无需读取 php://input
✅ 所有值均为字符串,注意类型转换(如 (int)$_POST['age'])
总结
| 特性 | application/x-www-form-urlencoded | application/json |
|---|---|---|
| PHP 自动解析 | ✅ 填充到 $_POST | ❌ 需手动读 php://input(框架底层) |
| 支持嵌套结构 | ❌(仅有限支持数组,如 key[]=val) | ✅ 完全支持对象、数组、嵌套等复杂结构 |
| 典型用途 | HTML 表单提交、传统 Web 请求 | API 接口、AJAX、前后端分离、开放平台(如饿了么、微信) |
| 发送时编码 | http_build_query($data) | json_encode($data, JSON_UNESCAPED_UNICODE) |
| 接收时变量 | 直接使用 $_POST | file_get_contents('php://input') + json_decode() |
三、数据库 JSON 字段的正确处理(ThinkPHP 等框架)
- 问题场景 : 你有一个模型字段如 phones 或 serving_time,类型为 VARCHAR / TEXT,但你在 PHP 中直接传入数组:
$model->serving_time = ['10:00-02:00']; // ❌ 错误:传入的是数组
$model->save();
结果:
ThinkPHP 不会自动将数组转为 JSON 字符串写入字符串字段;
数据可能被静默丢弃、存为 NULL 或空字符串。
正确做法:手动 json_encode()
$model->serving_time = json_encode(['10:00-02:00']); // ✅ 转为字符串
$model->save();
此时数据库实际存储值为:
["10:00-02:00"]
这是一个合法的 JSON 字符串,不含反斜杠。直接返回前端
返回 API 时的“双重转义”误解, 当你将该模型数据直接返回给前端:
return json($model); // ThinkPHP 自动 json_encode()
前端收到:
{
"serving_time": "[\"10:00-02:00\"]"
}
这并非错误!而是标准 JSON 行为:| 场景 | 内容 | 说明 |
|---|---|---|
| 数据库字段值 | ["10:00-02:00"] | ✅ 正确的 JSON 字符串,无 \ |
PHP var_dump() | string(17) "["10:00-02:00"]" | ✅ 真实内容,长度 17,无转义 |
| 前端收到的 JSON | "serving_time": "[\"10:00-02:00\"]" | ✅ 外层 json_encode() 对内层字符串中的 " 转义 |
💡 关键理解:" 是 JSON 标准要求的转义,不代表数据库里有反斜杠。
推荐返回方式:解码为数组再输出
若希望前端直接使用数组(而非字符串),应在返回前 json_decode():
$data = $model->toArray();
$data['serving_time'] = json_decode($data['serving_time'], true);
return json($data);
// 前端收到:{ "serving_time": ["10:00-02:00"] }四、php字符串拼接
在 PHP 中,只有「标量类型」(string、int、float、bool)可以安全地使用 . 操作符进行字符串拼接。
如果尝试拼接 非标量类型(如 array、object、resource 等),行为会因类型和 PHP 版本而异:
| 类型 | 拼接行为 | PHP 版本影响说明 |
|---|---|---|
string | ✅ 正常拼接 | 无 |
int | ✅ 自动转为字符串(如 123 → "123") | 无 |
float | ✅ 自动转为字符串(如 3.14 → "3.14") | 无 |
bool | ✅ true → "1",false → ""(空字符串) | 无 |
null | ✅ 被转为空字符串 ""(无错误) | 所有版本一致 |
array | ❌ 触发 TypeError(PHP 8.0+)或 Notice: Array to string conversion(PHP < 8.0) | PHP 8.0 起严格化,直接报错 |
object | ❌ 若未定义 __toString(),行为同 array;若定义了,则调用 __toString() | PHP 8.0+ 对无 __toString() 报错 |
resource | ❌ 触发类似 array 的错误 | 已废弃,不推荐使用 |
安全拼接示例(标量类型)
echo "Hello " . "world"; // "Hello world"
echo "Count: " . 42; // "Count: 42"
echo "Pi ≈ " . 3.1415; // "Pi ≈ 3.1415"
echo "Active: " . true; // "Active: 1"
echo "Active: " . false; // "Active: "(空)
echo "Value: " . null; // "Value: "(空,无错误)非标量类型
将非标量类型转换为JSON形式的字符串在进行拼接
$obj = new stdClass();
$obj->id = 1001;
$obj->status = 'active';
echo "Object data: " . json_encode($obj, JSON_UNESCAPED_UNICODE);
// 输出:Object data: {"id":1001,"status":"active"}五、PHP 数组与 JSON 结构对应关系说明
| PHP 写法 | 对应的 JSON 结构 | 说明 |
|---|---|---|
[] | [] | 空数组 → JSON 空数组 |
["a" => 1] | { "a": 1 } | 关联数组 → JSON 对象 |
[1, 2, 3] | [1, 2, 3] | 索引数组(连续数字键)→ JSON 数组 |
[ [ "a" => 1 ] ] | [ { "a": 1 } ] | 数组中包含一个对象 → JSON 数组里包一个对象 |
- 想要 JSON 对象? → 用 关联数组 ["key" => value]
- 想要 JSON 数组? → 用 索引数组 [value1, value2] 或 嵌套结构 [ [...], [...] ]
版权所有
版权归属:念宇
