一個content-type引發的問題
阿新 • • 發佈:2019-02-06
背景介紹:
問題:報警監控顯示專案的活動介面異常並報錯,提示json_decode解析異常,但不必現,無法立刻判斷原因。
先介紹下我們的資料傳遞流程:
【app客戶端】傳送加密資料 -------->【閘道器】解密-透傳------>【php伺服器】業務邏輯
圖1.app報錯頁面 監控時常顯示專案的某個介面報錯異常,報警提示如下:
圖2.報警郵件 過程分析: 很明顯,json_decode($_POST['result'])出錯,解析的資料格式非標準encode格式 那麼檢視下decode的資料唄,看是什麼問題 抓包app給閘道器的資料(正確):
圖3.原始訪問資料截圖 php收到的file_get_contents('php://input')資料(正常)
由圖看php原始資料記錄可得,閘道器傳遞時資料格式顯示是正常的,格式錯誤是因為php底層給$_POST賦值時(PHP生命週期第二步)錯誤造成,也就是說,是php的問題。但底層為什麼會錯亂呢? 問題解決: 兩種方式指定資料傳輸的Content-type都為:application/x-www-form-urlencoded 細心的同學應該發現一個區別,圖3中用postman模擬的資料,key對的value是被轉義掉的, php底層對$_POST賦值是根據&切割的,但是閘道器傳來的資料沒有轉義,所以當value中有"&"值時自動分開造成錯誤切割。 真相大白,原因是app在傳遞資料時只是做了加密處理,忘記做urlencode處理,閘道器透傳到php伺服器,造成php解析錯誤。找到真正的原因,那問題就好解決的, 根本解決方案:app做urlecode 臨時解決方案:php做正則處理,用file_get_contents('php://input')裡的方案。
知識延伸: 本次問題解決的過程中,曝露出了一個問題,對http的content-type並不是十分熟悉,藉此機會延伸學習一下。 Content-Type: 作用:說明了實體主體內物件的媒體型別(MIME) 包含型別:
著重看這麼幾個: 1.multipart/form-data 2.application/x-www-form-urlencoded 【預設型別】 3.application/json
1.multipart/form-data 就是http請求中的multipart/form-data,它會將表單的資料處理為一條訊息,以標籤為單元,用分隔符分開。既可以上傳鍵值對,也可以上傳檔案。當上傳的欄位是檔案時,會有Content-Type來表名檔案型別;content-disposition,用來說明欄位的一些資訊; 由於有boundary隔離,所以multipart/form-data既可以上傳檔案,也可以上傳鍵值對,它採用了鍵值對的方式,所以可以上傳多個檔案。 2.application/x-www-form-urlencoded 瀏覽器用x-www-form-urlencoded的編碼方式把form資料的value值urlencode,轉換成一個字串(name1=value1&name2=value2…),然後把這個字串append到url後面,用?分割,載入這個新的url。 3.raw -application/json 可以上傳任意格式的文字,可以上傳text、json、xml、html等 總結: 1.各php對各Content-Type的應用情況
所以,只要特殊記一下,
當Content-Type==json,用file_get_contents('php://input')接收並且json_decode,$_POST是無值的
當Content-Type==form/data,不要用file_get_contents('php://input')接受
2.form-data與urlencode小知識點
Content-Type:multipart/form-data body體傳送有邊界值
Content-Type:application/x-www-form-urlencoded
會把引數用&連結組裝,並且對value做轉義操作【很關鍵】,詳細解釋
3.php://input與$HTTP_RAW_POST_DATA區別 php://input訪問請求的原始資料的只讀流。不依賴於特定的 php.ini 指令 $HTTP_RAW_POST_DATA 包含 POST 提交的原始資料,不建議使用,php7已經被廢棄 4.php 的curl與 linux傳送curl,在www_url_encode是否會轉譯資料 問題: 1.php底層如何對$_POST進行賦值的,注意 Content-Type:multipart/form-data下的情況 2.file_get_contents('php://input');原理,為什麼在Content-Type:multipart/form-data下無法捕獲到值 參考地址: 資料互動截圖記錄:
圖1.multipart/form-data
圖2.application/x-www-form-urlencoded
圖3.application/json
圖1.app報錯頁面 監控時常顯示專案的某個介面報錯異常,報警提示如下:
圖2.報警郵件 過程分析: 很明顯,json_decode($_POST['result'])出錯,解析的資料格式非標準encode格式 那麼檢視下decode的資料唄,看是什麼問題 抓包app給閘道器的資料(正確):
php收到閘道器的$_POST資料(錯亂),answer=[{"answer":"https://h5.youzan.com/v2/goods/277ola8ay4b6x?reft=1512028692491&spm=f47826345&sf=wx_menu"}]
"answer":"[{\"answer\":\"https:\/\/h5.youzan.com\/v2\/goods\/277ola8ay4b6x?reft=1512028692491","spm":"f47826345","sf":"wx_menu\"}]"
顯然經過閘道器後,【answer欄位裡的值結構發生了變化】,導致解析失敗。
所以就是【閘道器】的問題唄。
結論為時過早,還真不全是他的問題
圖3.原始訪問資料截圖 php收到的file_get_contents('php://input')資料(正常)
answer=[{"answer":"https://h5.youzan.com/v2/goods/277ola8ay4b6x?reft=1512028692491&spm=f47826345&sf=wx_menu"}]
由圖看php原始資料記錄可得,閘道器傳遞時資料格式顯示是正常的,格式錯誤是因為php底層給$_POST賦值時(PHP生命週期第二步)錯誤造成,也就是說,是php的問題。但底層為什麼會錯亂呢? 問題解決: 兩種方式指定資料傳輸的Content-type都為:application/x-www-form-urlencoded 細心的同學應該發現一個區別,圖3中用postman模擬的資料,key對的value是被轉義掉的, php底層對$_POST賦值是根據&切割的,但是閘道器傳來的資料沒有轉義,所以當value中有"&"值時自動分開造成錯誤切割。 真相大白,原因是app在傳遞資料時只是做了加密處理,忘記做urlencode處理,閘道器透傳到php伺服器,造成php解析錯誤。找到真正的原因,那問題就好解決的, 根本解決方案:app做urlecode 臨時解決方案:php做正則處理,用file_get_contents('php://input')裡的方案。
$raw_str =file_get_contents('php://input'); preg_match("/&answer=(.*)}]&/",$raw_str,$res); $answer = empty($res[1])? Yii::$app->getRequest()->post('answer') : $res[1].'}]';
知識延伸: 本次問題解決的過程中,曝露出了一個問題,對http的content-type並不是十分熟悉,藉此機會延伸學習一下。 Content-Type: 作用:說明了實體主體內物件的媒體型別(MIME) 包含型別:
text/html : HTML格式
text/plain :純文字格式
text/xml : XML格式
image/gif :gif圖片格式
image/jpeg :jpg圖片格式
image/png:png圖片格式
以application開頭的媒體格式型別:
application/xhtml+xml :XHTML格式
application/xml : XML資料格式
application/atom+xml :Atom XML聚合格式
application/pdf :pdf格式
application/msword : Word文件格式
application/octet-stream : 二進位制流資料(如常見的檔案下載)
application/json : JSON資料格式
application/x-www-form-urlencoded :form表單資料被編碼為key/value格式傳送到伺服器(表單預設的提交資料的格式)
另外一種常見的媒體格式是上傳檔案之時使用的:
multipart/form-data : 需要在表單中進行檔案上傳時,就需要使用該格式
著重看這麼幾個: 1.multipart/form-data 2.application/x-www-form-urlencoded 【預設型別】 3.application/json
1.multipart/form-data 就是http請求中的multipart/form-data,它會將表單的資料處理為一條訊息,以標籤為單元,用分隔符分開。既可以上傳鍵值對,也可以上傳檔案。當上傳的欄位是檔案時,會有Content-Type來表名檔案型別;content-disposition,用來說明欄位的一些資訊; 由於有boundary隔離,所以multipart/form-data既可以上傳檔案,也可以上傳鍵值對,它採用了鍵值對的方式,所以可以上傳多個檔案。 2.application/x-www-form-urlencoded 瀏覽器用x-www-form-urlencoded的編碼方式把form資料的value值urlencode,轉換成一個字串(name1=value1&name2=value2…),然後把這個字串append到url後面,用?分割,載入這個新的url。 3.raw -application/json 可以上傳任意格式的文字,可以上傳text、json、xml、html等 總結: 1.各php對各Content-Type的應用情況
3.php://input與$HTTP_RAW_POST_DATA區別 php://input訪問請求的原始資料的只讀流。不依賴於特定的 php.ini 指令 $HTTP_RAW_POST_DATA 包含 POST 提交的原始資料,不建議使用,php7已經被廢棄 4.php 的curl與 linux傳送curl,在www_url_encode是否會轉譯資料 問題: 1.php底層如何對$_POST進行賦值的,注意 Content-Type:multipart/form-data下的情況 2.file_get_contents('php://input');原理,為什麼在Content-Type:multipart/form-data下無法捕獲到值 參考地址: 資料互動截圖記錄:
圖1.multipart/form-data
圖2.application/x-www-form-urlencoded
圖3.application/json