編碼的世界 / 優質文選 / 文明

PHP處理POST參數帶&符號導致數據被截斷的問題


2021年7月05日
-   

如果API接口的參數數據中,出現&符號,php在處理POST數據時,則會自動截斷數據流,導致該參數的數據不完整,並且新增一個並不受歡迎的參數,進而導致延簽失敗,接口拿不到期望的數據等異常。
比如:
curl "https://www.example.com/index.php" -d 'money=69.0&order_name=0010195102246107133&products=[{"discount":49,"num":1,"price":118,"product_name":"樂家特級初榨橄欖油&摩典納香醋禮盒裝500ml*2","product_no":"21562075"}]&qrcode_str=6226158790233247708&score_money=69.0&service=user.cutMoney&source=cityshop&timestamp=1578378620686&type=1&version=1.0&sign=bde2e7224f804d24767bdc817dd0fee6'

打印$_POST,獲取到的數據是:
array (
'money' => '69.0',
'order_name' => '0010195102246107133',
'products' => '[{"discount":49,"num":1,"price":118,"product_name":"樂家特級初榨橄欖油',
'摩典納香醋禮盒裝500ml*2","product_no":"21562075"}]' => '',
'qrcode_str' => '6226158790233247708',
'score_money' => '69.0',
'service' => 'user.cutMoney',
'source' => 'cityshop',
'timestamp' => '1578378620686',
'type' => '1',
'version' => '1.0',
'sign' => 'bde2e7224f804d24767bdc817dd0fee6',
)

以上數據,顯然是我們不願意看到的。
上面示例請求報文中,發現參數products為json數據,且裏面字段product_name帶有&符,遇到此問題,通常做法是,運用base64編碼對特殊報文進行編碼,請求方編碼,接口方解碼,這是規範的處理流程。
但具體實踐中,如果請求方不方便做系統升級,諸如app、收銀機系統等,那麼我們只能通過後端來處理此類棘手問題,
思路:我們應該想到使用正則來處理請求參數,避免使用$_POST全局變量或系統框架提供的參數來處理請求數據;

方案一:


為接口參數定義正則表達式,通過正則去獲取參數,優點是可以按照現有參數列表獲取正確的參數值,但接口增減參數,需要配合修改,否則會報錯
<?php
$stream = file_get_contents("php://input");
$args = ['money' => '(.*?)&', 'order_name' => '(.*?)&', 'products' => '([.*?])&', 'qrcode_str' => '(.*?)&', 'score_money' => '(.*?)&', 'service' => '(.*?)&', 'source' => '(.*?)&', 'timestamp' => '(.*?)&', 'type' => '(.*?)&', 'version' => '(.*?)&', 'sign' => '(.*?$)'];
foreach ($args as $arg => $pattern) {
if (preg_match('/'.$arg.'='.$pattern.'/', $stream, $matches)) {
$args_param[$arg] = $matches[1];
} else {
$args_param[$arg] = '';
}
}
if ($args_param['service'] == 'user.cutMoney') {
$post_param = $args_param;
} else {
$post_param = $_POST;
}
var_dump($post_param);

方案二:


單獨處理帶有&符的參數,並且unset掉異常的參數,優點是,不影響接口增減參數,但異常參數可能處理不幹淨,比如這樣一個字符串
樂家特級初榨橄欖油&bug_test&摩典納香醋禮盒裝500ml*2
如遇到這類的字符流,則會新增一個看似規範的bug_test參數,導致系統難以發現它,
<?php
$post_param = $_POST;
if ($post_param['service'] == 'user.cutMoney') {
if (preg_match('/products=([.*?])&/', $stream, $matches)) {
$post_param['products'] = $matches[1];
}
foreach ($post_param as $key => $val) {
if(strpos($key, "]") || strpos($key, "}")) {
unset($post_param[$key]);
}
}
}
var_dump($post_param);

以上只是臨時處理方案,規範的解決辦法還是兩邊統一編碼解碼

熱門文章