iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
WordPress

從 0 到 100:WordPress 開發者的實戰手冊系列 第 17

Day 17 - WordPress 的 AJAX 設計與應用

  • 分享至 

  • xImage
  •  

AJAX 是 Asynchronous JavaScript and XML 的縮寫,它並不是一種新的技術,而是綜合了多項瀏覽器端網頁開發技術的方法。字面上是非同步的 JavaScript 與 XML 技術,不過實務上廣泛地使用 JSON 資料格式來做交換,不限於 XML。

簡單來說,就是用 JavaScript 來達成不需要重新載入整個頁面,就可以從後端拿到資料後,更新網頁上的內容。

流程

AJAX 流程圖
圖:AJAX 流程圖

  1. 用戶在網頁上進行點擊了「按紐」、「捲軸滑動」等等互動行為。
  2. 互動行為觸發了 JavaScript 來進行 AJAX 請求。
  3. JavaScript 向伺服器發送非同步的 HTTP 請求。
  4. 負責這個路由進來的請求的後端程式,處理完請求,回傳結果給 JavaScript。
  5. JavaScript 根據收到的資料,更新網頁的部分內容。
  6. 使用者此時可以看到網頁已更新的部分,而整個網頁並未重新載入。

模擬情境

情境
圖:情境流程

用身為一個 WordPress 使用者的情境,可能會是這樣:

使用者 Terry 在網站上的訂閱電子報輸入框輸入 Email,並按下訂閱按紐,接著後端分析 Email 正確,把此 Email 加入電子報寄發清單,並回覆訂閱成功訊息給前端。Terry 看到電子報的輸入框消失,變成您已訂閱成功字樣,心滿意足地離開網頁...。

WordPress 核心的 AJAX 處理機制

在 WordPress 的 AJAX 溝通的的主要入口為 admin-ajax.php 這個檔案,雖然它的檔名上有 admin 這個單字,位置在 wp-admin 目錄之下,乍看之下會讓人覺得,它是不是只用在後台管理?

或許初期是為了後台而設計,所以才被放在 wp-amdin 目錄中,不過它的使用範圍不僅限於後台管理員區域的請求。前端的 AJAX 請求也適用。

動態鉤點

在介紹 admin-ajax.php 檔案是如何處理 AJAX 請求之前先來認識 WordPress 中很常見的鉤點命名的方式—「動態鉤點」 (dynamic hook)。

動態鉤點並不是和一般的鉤點一樣,直接命名在程式碼中,而是它的名稱中某一部分是字串變數,由傳入的變數來決定它最後的名稱。

官方文件鉤點查詢頁面
圖:官方文件鉤點查詢頁面 文件

例如,查詢官方文件,會發現有一個鉤點名稱長這樣:

wp_ajax_{$action}

鉤點名稱中的 {$action} 在這份文件中的意思為可替換的變數。這樣說感覺有點抽像不好懂,讓我們實際來看看 admin-ajax.php 中的程式碼片段。

wp-admin/admin-ajax.php 檔案尾端內容
圖:wp-admin/admin-ajax.php 檔案尾端內容

在 admin-ajax.php 的內容尾端如下:

第 1 行:$_REQUEST['action'] 可以讀取到 $_GET 以及 $_POST 裡鍵值為 action 的變數,稍後看到 do_action 函式埋的鉤點全名為 wp_ajax_wp_ajax_nopriv_ 與此字串的組合。。

鉤點:wp_ajax_{$action}

第 17 行:定義了此鉤點,是使用者登入時才會觸發的鉤點。不過在這之前的第 5 行至第 7 行,如果動態的鉤點中沒有任何被註冊的函式,則會回應 HTTP 狀態碼 400

鉤點:wp_ajax_nopriv_{$action}

第 32 行:定義了此鉤點,是使用者未登入時,才會觸方的鉤點。不過在這之前的第 20 行至第 22 行,如果動態的鉤點中沒有任何被註冊的函式,則會回應 HTTP 狀態碼 400

實例

為了測試,我們先在佈景主題裡的 functions.php 底部寫一下測試用的程式碼,來看看結果如何。

佈景主題 functions.php 測試
圖:佈景主題 functions.php 測試 (Gist

第 6 行至第 23 行:將測試用的 JavaScript 檔案載入。
第 7 行:啟用 jQuery 依賴,讓 JavaScript 檔案使用。
第 9 行和第 17 行定義的 script ID 要一致。
第 18 行:定義一個全域的 demo_object 物件在 window 物件下。
第 19 至 21 行:定義變數,放在第 18 行定義的物件中,供前端使用。
第 30 至 33 行:這是負責處理 AJAX 請求的程式碼。
第 35 行:註冊載入 JavaScript 檔案的鉤點。
第 36 行:註冊使用者已登入狀態下,處理 AJAX 請求的鉤點。
第 37 行:註冊使用者未登入狀態下,處理 AJAX 請求的鉤點。

注意:鉤點的名稱結合了 $action 變數。

JavaScript 檔案內容
圖:JavaScript 檔案內容

這一份是 JavaScript 檔案,重點在

第 2 行:在 PHP 定義給前端用的物件。
第 9 行: action 是決定動態鉤點名稱的變數。

在測試頁面的輸出以下 HTML。

<button id="simple-button">按這裡測試</button>

點擊即可看到跳出視窗訊息「這是 2023 鐵人賽的範例唷!」。

安全性

上述的例子並沒有考慮到檢查請求是否合法,考量到安全性的問題,必須使用 WordPress 的 nonce 功能來驗證請求,降低被 XSS (Cross-site Scripting, 跨站腳本攻擊) 的風險。

函式名稱 說明
wp_create_nonce 產生一個臨時性的字串,用來驗證請求或確認提交表單是來自網站本身,而不是外部的請求。
check_ajax_referer 驗證 AJAX 請求中的 nonce 值,確保請求是合法的。常用於 AJAX 安全性驗證。
wp_verify_nonce 驗證 nonce 值是否有效,主要用於非 AJAX 的請求,如表單提交。不過也可以用來驗證 AJAX 請求。

讓我們使用上述函式來修改之前的範例。

佈景主題 functions.php 測試
圖:佈景主題 functions.php 測試

第 21 行:使用 wp_create_nonce 函式,產生 nonce 並放進 demo_object 這個給前端使用的變數。
第 32 行:使用 check_ajax_referer 來驗證 nonce。第二個參數名稱是 nonce 的欄位名稱,test_nonce 是故意取名的,搭配著 JS 檔案的程式碼比較好理解。如果不指定欄位名稱的話,預設的欄位名稱為 _wpnonce

JavaScript 檔案內容
圖:JavaScript 檔案內容

第 10 行:指定 nonce 的欄位名稱。如果在驗證用的 PHP 程式碼沒有指定欄位名稱,這裡使用欄位名稱為 _wpnonce

改用 wp_verify_nonce 方法驗證
圖:改用 wp_verify_nonce 方法驗證

這邊是使用 wp_verify_nonce 進行驗證的版本,是麻煩了些,因為還需先檢查全域的 $_POST 或 $_GET 變數有沒有存在該資料所使用的鍵值。

總結

今天主要介紹了 WordPress 使用註冊 AJAX 動態鉤點來實作 AJAX 設計及使用方法,很重要的一點,一定要經過驗證 nonce 的過程,以提高前端與後端進行資料交換的安全性喔。那麼除了這個作法以為,還有其它的方式嗎?有的,那就是 REST API。在明天的文章中,筆者會介紹 WordPress 的 REST API 機制,及和傳統作法之間的比較喔。


課後思考:

除了訂閱電子報的例子之外,還有哪些網站功能常會使用 AJAX?

前篇解答參考:

過濾輸出的字串以達到最基本的跨站腳本攻擊的防護。另外輸入的部分更是必須嚴格過濾。如果好奇查看一下網站伺服器的 access log,會發現每天網站上造訪的不是只有人類,有時候還會發現惡意的網路爬蟲,這些來者不善的機器人一直試著塞資料來測試是否能達成 SQL注入 (SQL injection) 來造成漏洞,以取得資料庫的權限,更甚者是整個網站的權限。因此無論是資料的輸出和輸入,都必須嚴格檢查,好好把關。


上一篇
Day 16 - 佈景主題設計實戰 (8) 提交上架前的準備事項
下一篇
Day 18 - WordPress 的 REST API 設計與應用
系列文
從 0 到 100:WordPress 開發者的實戰手冊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言