0%

Flask 筆記 | 回應方式

網站互動過程的其中一環是使用者透過前端傳送要求至後端,後端根據取得資訊進行處理。不過先前我們僅有透過字串回傳給前端,事實上還有其他的回傳內容,一律統稱這些後端回傳資料至前端的過程為回應方式 (response)

情境 使用方式
API 回應資料 jsonify()
簡單訊息、測試 直接回傳字串
要控制 headers/status/cookies Response()
HTML 網頁渲染 render_template()
要導向頁面 redirect()
發生錯誤 abort() 或 回傳錯誤碼

回應方式有百百種,這邊我們只會針對特定幾個說明。

JSON 格式字串

JSON (JavaScript Object Notation) 格式字串(下文以 JSON 簡稱)是:

一種以字串形式儲存結構化資料的格式,通常用來在不同系統(例如前端與後端)之間傳遞資料。

更精確一點來說,JSON 是一種資料表示格式,本質上是一段文字字串,但遵守特定的語法規則,可以用來表達:

  • 鍵值對(如字典): { "name": "David" }
  • 陣列(如列表):[1, 2, 3]
  • 巢狀結構: { "user": { "id": 1, "tags": ["a", "b"] } }

例如以下就是一個合法的 JSON 範例:

1
2
3
4
5
6
{
"name": "Anthony",
"age": 25,
"is_student": true,
"languages": ["Mandarin", "Taiwanese"]
}

在 Python 中,要把字典(或其他可序列化的資料)轉成 JSON,使用標準函式庫 json 中的 json.dumps() 函式即可,語法為:

1
2
3
import json

json_string = json.dumps(data, ensure_ascii=True, indent=None)
參數 預設值 說明
ensure_ascii True 是否將非 ASCII 字元(如中文)轉為 \uXXXX 編碼
indent None 若指定為整數,會產生縮排格式的 JSON(常用於輸出檔案)
separators (',', ':') 用來壓縮 JSON(如移除空格)
sort_keys False 是否根據鍵的名稱排序輸出

例如在原本的根路徑根據使用者的語言偏好回傳 JSON,內容包含當前狀態與訊息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@app.route("/")
def index():
lang = request.headers.get('accept-language')

if lang.startswith("en"):
return json.dumps({
"status": "ok",
"messeage": "Hello, Flask"
})
else:
return json.dumps({
"status": "ok",
"message": "哈囉,Flask"
})

由於我瀏覽器語言偏好是繁體中文,因此會跑到第二個判斷式。但是!執行之後會發現出現奇怪的東西:

以 ASCII 編碼處理中文

圖 1:以 ASCII 編碼處理中文

原因是沒有指定 ensure_ascii=False,所以它會使用預設值 True,也就是將中文用 ASCII 編碼處理中文,變成 Unicode 編碼形式,對人類來說不易閱讀,但對電腦來說是合法且通用的 JSON 格式。因此只要將其設定為不要用 ASCII 處理中文即可避免此問題:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@app.route("/")
def index():
lang = request.headers.get('accept-language')

if lang.startswith("en"):
return json.dumps({
"status": "ok",
"messeage": "Hello, Flask"
})
else:
return json.dumps({
"status": "ok",
"message": "哈囉,Flask"
}, ensure_ascii=False) # 不要用 ASCII 編碼處理中文

執行後即可發現變回正常。

關閉 ASCII 編碼處理中文

圖 2:關閉 ASCII 編碼處理中文

不過 ensure_ascii 的設定也並非每種情況皆可使用,必須視不同情境而定:

情境 建議設定
後端 API 回傳給前端 建議加上 ensure_ascii=False,前端直接看到原始文字
要寫入檔案、存資料庫 視乎需求,若預期多語言支援,保留原文有助辨識
要跨系統傳輸(尤其含不同語言/系統) 預設 True 比較保險(兼容性高)
要除錯、檢查內容時 設為 False 更容易閱讀

重新導向

重新導向是指伺服器在收到請求後,不直接提供內容,而是回應一個「請轉到其他位置」的訊息給用戶端(例如瀏覽器)。瀏覽器接收到這個訊息後,會自動發出新的請求到指定的網址。這種機制很常見,例如:

  • 使用者尚未登入 → 導向登入頁面
  • 根目錄 / → 導向 /zh//en/
  • 資料已搬遷 → 導向新網址

Flask 提供了 redirect() 工具來快速實作這件事。語法為:

1
2
3
4
5
6
from flask import redirect

@app.route("路徑")
def 處理函式名稱:
處理邏輯
return redirect("/網址路徑")

預設會產生 HTTP 302 Found 回應,告訴瀏覽器「請重新發送請求到新網址」。例如以下程式碼會根據瀏覽器的語言偏好,自動將使用者導向對應語言的分頁:

  • 若語言為英文開頭(如 en-US),則導向 /en/
  • 否則導向 /zh/
1
2
3
4
5
6
7
8
@app.route("/")
def index():
lang = request.headers.get('accept-language')

if lang.startswith("en"):
return redirect("/en/")
else:
return redirect("/zh/")

/en//zh/ 分別由:

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.route("/en/")
def index_en():
return json.dumps({
"status": "ok",
"messeage": "Hello, Flask"
})

@app.route("/zh/")
def index_zh():
return json.dumps({
"status": "ok",
"message": "哈囉,Flask"
}, ensure_ascii=False)

提供對應語系的 JSON 回應。執行後可以看到以下畫面:

重新導向

圖 3:重新導向

導向時發生了什麼事?

從伺服器 log 可以看到:

1
2
3
4
 * Running on http://127.0.0.1:5000
INFO:werkzeug:Press CTRL+C to quit
INFO:werkzeug:127.0.0.1 - - [05/Aug/2025 12:08:51] "GET / HTTP/1.1" 302 -
INFO:werkzeug:127.0.0.1 - - [05/Aug/2025 12:08:51] "GET /zh/ HTTP/1.1" 200 -

表示使用者原本請求根目錄 /,伺服器回傳 302 redirect,告訴用戶端請求 /zh/,瀏覽器自動重發請求至 /zh/,成功收到回應