問題描述
最近在使用步數統計插件(WeFootStep)時,發現網站首頁完全變成了一段JSON數據,而不是正常的HTML頁面。具體表現為首頁顯示如下內容:
{"results":"<li><a href=\"https:\/\/blog.ybyq.wang\/archives\/186.html\">\u770b\u770b\u4f60\u662f\u4e0d\u662f\u201c\u8d5e\u535a\u6587\u76f2\u201d<p class=\"text-muted\">dobe\u3001IDEA\u3001<mark class='text_match'>pycharm<\/mark>\u7b49\r\n<\/p><\/a><\/li>..."}
這完全破壞了網站的正常瀏覽體驗。
原因分析
經過幾天的艱苦排查,發現問題出在WeFootStep插件的Widget.php
文件中的getStepDataJson()
函數:
/*** 獲取步數JSON數據,用于AJAX請求*/
public function getStepDataJson()
{$history = $this->getStepHistory();$stats = $this->getStepStats();$data = ['history' => $history,'stats' => $stats];header('Content-Type: application/json');echo json_encode($data);exit;
}
這個函數存在的問題是:
- 函數直接設置了響應頭為
application/json
- 輸出JSON編碼后的數據
- 調用
exit
終止了PHP的執行流程
這種實現方式本來是為AJAX請求設計的,但如果在普通頁面加載過程中被誤調用,就會導致整個頁面只輸出JSON數據。
解決方案
解決方案很簡單:修改getStepDataJson()
函數,讓它只在確認是AJAX請求時才直接輸出JSON并退出:
/*** 獲取步數JSON數據,用于AJAX請求*/
public function getStepDataJson()
{$history = $this->getStepHistory();$stats = $this->getStepStats();$data = ['history' => $history,'stats' => $stats];// 只在AJAX請求時才直接輸出JSON并退出if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {header('Content-Type: application/json');echo json_encode($data);exit;}// 如果不是AJAX請求,返回數據而不直接輸出return $data;
}
這個改進添加了一個檢查機制,通過判斷$_SERVER['HTTP_X_REQUESTED_WITH']
是否為xmlhttprequest
來確定當前是否是一個AJAX請求。如果是,才執行原來的行為;如果不是,則只返回數據而不直接輸出。
技術要點
-
AJAX請求的識別:通常AJAX請求會包含
X-Requested-With: XMLHttpRequest
頭,這是檢測AJAX請求的標準方法。 -
避免直接輸出:在MVC架構的應用中,控制器方法通常不應該直接輸出內容,而是返回數據讓框架處理。
-
避免無條件退出:
exit
或die
會立即終止PHP的執行,應謹慎使用,尤其是在可能被其他代碼調用的函數中。
教訓與最佳實踐
在開發PHP插件或組件時,應遵循以下原則:
-
關注點分離:數據處理與輸出應該分開,不要在獲取數據的函數中直接輸出。
-
條件性輸出:如果必須在函數中輸出,應該有明確的條件控制。
-
防御性編程:總是假設你的函數可能在意外的情況下被調用,添加適當的檢查。
-
明確文檔:清晰記錄函數的行為和副作用,特別是那些會改變HTTP頭或直接輸出的函數。
這個簡單的修改解決了首頁顯示JSON數據的問題,也提醒我們在插件開發中要注意代碼的健壯性和兼容性。
作者:xuan
個人博客:https://blog.ybyq.wang
原文鏈接:https://blog.ybyq.wang/archives/770.html
歡迎訪問我的博客,獲取更多技術文章和教程。