React Native fetch 預設發送 Content-Type: application/json,導致 PHP $_POST 為空。本文分析根因,提供前端構建表單數據、後端讀取 php://input 兩類方案,並給出生產級統一兼容中間件代碼(支援iOS/Android/Web多端共存)。
fetch('https://api.example.com/endpoint', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json', // 關鍵:JSON格式
},
body: JSON.stringify({ username: 'val', pass: 'val' })
});
PHP $_POST 僅自動解析 application/x-www-form-urlencoded 或 multipart/form-data,不解析 JSON,故為空陣列。
function toQueryString(obj) {
return Object.keys(obj).sort().map(key => {
const val = obj[key];
if (Array.isArray(val)) {
return val.sort().map(v => `${encodeURIComponent(key)}=${encodeURIComponent(v)}`).join('&');
}
return `${encodeURIComponent(key)}=${encodeURIComponent(val)}`;
}).join('&');
}
// fetch body: toQueryString(obj)
// headers['Content-Type'] = 'application/x-www-form-urlencoded'
$json = json_decode(file_get_contents('php://input'), true);
var_dump($json['username']); // 可正常獲取
場景:API已上線服務Web/Android,新增RN端不想改動現有介面。
// 統一入口(如框架的 beforeAction / 中間件)
// 1. 識別客戶端類型(約定 Header: App: ios-appname-1.8)
if (!function_exists('apache_request_headers')) {
$appName = $_SERVER['HTTP_APP'] ?? ''; // Nginx下大寫+HTTP_前綴
} else {
$headers = apache_request_headers();
$appName = $headers['App'] ?? '';
}
// 2. RN專用:JSON body 解析注入 $_POST
if ($appName === 'your-rn-app-identifier') {
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (is_array($data)) {
$_POST = array_merge($_POST, $data); // 合併不覆蓋
}
}
const App = {
config: { api: 'https://api.example.com', version: '1.1', debug: true },
serialize(obj) { /* ... */ },
random() { return Date.now() + Math.floor(Math.random()*9999); },
send(url, options = {}) {
const defaults = {
method: 'GET',
headers: {
'Authorization': 'token',
'Accept': 'application/json',
'Content-Type': 'application/json',
'App': 'your-rn-app-identifier', // 關鍵:後端識別用
},
data: { _regq: this.random() }, // 防快取
dataType: 'json',
};
const opts = Object.assign({}, defaults, options);
let fullUrl = this.config.api + url;
if (opts.method === 'GET') {
fullUrl += '?' + this.serialize(opts.data);
}
if (opts.method !== 'GET') {
opts.body = JSON.stringify(opts.data);
}
return fetch(fullUrl, opts)
.then(r => r.json())
.then(res => {
if (res.errcode === 101) return this.doLogin();
if (res.errcode !== 0) return this.handleError(res);
return opts.success(res, true);
})
.catch(err => console.warn(err));
},
// 使用:App.send('/api/user', { success: (res, ok) => {} })
};
Content-Type: application/json 導致 PHP 不自動填充 $_POSTjson_decode(file_get_contents('php://input'), true)App Header 識別客戶端,中間件統一注入 $_POST