FastAPI 是一個高效能的 Python Web 框架,而 Jinja2 作為其推薦的範本引擎,能有效地簡化網頁應用開發。本文將介紹如何整合 Jinja2 與 FastAPI,並探討範本渲染、變數傳遞、迴圈、條件判斷和靜態資源管理等核心技巧。透過 Jinja2Templates 和 TemplateResponse,我們能輕鬆地將 Python 資料動態地嵌入 HTML 範本。同時,文章也涵蓋了路徑引數的運用,以及如何傳遞和處理複雜的資料結構,例如字典。此外,我們將學習如何在 Jinja2 範本中使用條件陳述式和迴圈結構,實作更精細的網頁邏輯控制。最後,文章還將介紹如何有效地管理和使用靜態資源,例如 JavaScript 檔案、圖片和 CSS 樣式表,讓網頁應用更具完整性和互動性。
FastAPI 中的範本引擎應用
HTML 回應與範本引擎簡介
在前面的章節中,我們已經瞭解 HTMLResponse 是 Response 類別的子類別,主要用於處理 HTML 內容的回應。HTMLResponse 的 content 引數接受一個字串(或位元組字串),並需要從 fastapi.responses 模組匯入。此外,我們還需要將其設定為 @app.get() 裝飾器的 response_class 引數值。
使用 HTMLResponse 類別的範例
清單 4-3 展示瞭如何使用 HTMLResponse 類別,並定義了一個路徑引數 name 傳遞給 index() 函式。其目的是從 URL 路徑中解析該引數,以便在瀏覽器中存取 http://localhost:8000/Rahul 時,函式能夠呈現 “Hello Rahul” 的文字。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/{name}", response_class=HTMLResponse)
async def index(name):
ret = '''
<html>
<body>
<h2 style="text-align: center;">Hello {}!</h2>
</body>
</html>
'''.format(name)
return HTMLResponse(content=ret)
程式碼解析:
- 使用
str類別的format()方法將路徑變數的值插入到字串中的佔位符。 - 瀏覽器的 HTML 解析引擎負責處理標籤及其屬性,從而顯示如圖 4-1 所示的結果。
範本引擎的工作原理
HTML 是用於構建網頁的語言,本質上是靜態的。雖然我們在前面的範例中透過將 HTML 程式碼的字串表示與變數部分混合來增加了一定的互動性,但這種方法非常繁瑣。想像一下,如果需要有條件地顯示表格資料,將會有多麼困難。
大多數現代網頁應用框架都使用網頁範本系統來實作此目的。範本引擎(或稱範本處理器)接收範本和資料來源,將資料來源中的資料項逐一放入範本中的相應佔位符,從而動態生成多個網頁。圖 4-2 說明瞭範本引擎的工作原理。
範本引擎的組成部分:
- 範本引擎:負責處理範本和資料來源。
- 資料來源:提供要插入範本的資料,可以是資料函式庫表格、記憶體陣列或 CSV 檔案等。
- 網頁範本:包含一個或多個範本語言程式碼區塊的網頁,用於填充動態資料。
在 FastAPI 中使用 Jinja2 範本引擎
FastAPI 並未依賴特定的範本引擎,但推薦使用 jinja2 套件。由於 jinja2 預設未安裝,因此需要使用 pip3 install jinja2 命令進行安裝。
Jinja2 的特點:
- 快速且輕量:適合用於網頁應用的範本渲染。
- 沙箱環境:禁止使用潛在的不安全資料,有效防止跨站攻擊。
- 範本繼承:強大的功能,有助於保持網頁設計的一致性。
建立 “Hello World” 範本
首先,將 HTML 字串儲存為 hello.html(清單 4-4),並放置在與 app.py 指令碼相同的 templates 資料夾中。
<html>
<body>
<h2 style="text-align: center;">Hello World!</h2>
</body>
</html>
使用 Jinja2Templates 設定範本物件
FastAPI 對 jinja2 的支援以 Jinja2Templates 函式的形式提供,該函式定義在 fastapi.templating 模組中。我們需要匯入並使用它來建立範本物件(清單 4-5)。
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
使用 TemplateResponse 渲染範本
在操作函式中,需要接收 request 物件引數,並將其作為請求上下文傳遞給範本(清單 4-6)。
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse("hello.html", {"request": request})
資料夾結構:
app/
│ main.py
│
└───templates/
hello.html
最終程式碼(清單 4-7):
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse("hello.html", {"request": request})
隨著網頁應用的複雜度不斷增加,使用範本引擎來管理動態內容已成為一種標準做法。FastAPI 與 Jinja2 的結合為開發者提供了一個強大且靈活的工具,用於構建現代網頁應用。未來,我們可以期待更多的功能擴充套件和效能最佳化,以滿足不斷變化的開發需求。
圖表說明:範本引擎工作原理
圖表翻譯: 此圖示展示了範本引擎的工作原理。資料來源和網頁範本被輸入到範本引擎中,經過處理後生成動態網頁。這種機制使得網頁內容能夠根據不同的資料來源動態變化,提高了網頁應用的靈活性和可維護性。
詳細解說:
- 資料來源:可以是資料函式庫、記憶體中的資料結構或是外部檔案等。
- 網頁範本:定義了網頁的基本結構,並包含用於插入動態內容的佔位符。
- 範本引擎:負責將資料來源中的資料填充到網頁範本中,生成最終的動態網頁。
- 動態網頁:根據不同的資料來源生成的不同網頁內容,供使用者存取。
這種機制在現代網頁應用中非常常見,能夠有效地分離網頁的結構和內容,提高開發效率和網頁的可維護性。
範本引擎與動態內容渲染
在現代Web開發中,範本引擎扮演著至關重要的角色。它允許開發者將應用程式的邏輯與呈現層分離,從而提高程式碼的可維護性和可讀性。本章節將探討FastAPI框架中範本引擎的使用,特別是Jinja2範本語言的應用。
靜態與動態範本渲染
首先,讓我們從一個簡單的例子開始。考慮以下基本的HTML範本:
<html>
<body>
<h2 style="text-align: center;">Hello World!</h2>
</body>
</html>
這個範本可以透過FastAPI的TemplateResponse方法渲染:
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse("hello.html", {"request": request})
內容解密:
Jinja2Templates類別用於初始化範本引擎,並指定範本檔案所在的目錄。TemplateResponse方法負責渲染指定的範本檔案,並將必要的上下文資料傳遞給範本。- 在這個例子中,我們傳遞了
request物件,這是Jinja2範本引擎所必需的。
路徑引數與範本變數
為了使範本更加動態,我們可以將路徑引數傳遞給範本。修改後的檢視函式如下所示:
@app.get("/{name}", response_class=HTMLResponse)
async def index(request: Request, name: str):
return templates.TemplateResponse("hello.html", {"request": request, "name": name})
對應的範本檔案hello.html可以修改為:
<html>
<body>
<h2 style="text-align: center;">Hello {{ name }}!</h2>
</body>
</html>
內容解密:
- 在檢視函式中,我們將
name引數新增到上下文字典中。 - 在範本中,我們使用雙大括號
{{ name }}來參照這個變數。 - Jinja2範本引擎會在渲染時將
name變數替換為實際的值。
傳遞複雜資料結構
除了簡單的變數外,我們還可以將複雜的資料結構(如字典)傳遞給範本。考慮以下例子:
@app.get("/employee/{name}/{salary}", response_class=HTMLResponse)
async def employee(request: Request, name: str, salary: int):
data = {"name": name, "salary": salary}
return templates.TemplateResponse("employee.html", {"request": request, "data": data})
對應的employee.html範本:
<html>
<body>
<h1>Employee Details</h1>
<h2>Name: {{ data.get('name') }} Salary: {{ data.get('salary') }}</h2>
</body>
</html>
內容解密:
- 我們建立了一個包含員薪水訊的字典
data。 - 在範本中,我們使用
data.get('key')語法來存取字典中的值。 - 這種方法允許我們在範本中安全地存取資料,即使某些鍵不存在也不會導致錯誤。
條件陳述式在範本中的應用
Jinja2範本語言支援條件陳述式,使我們能夠根據特定的條件渲染不同的內容。以下是一個例子:
<html>
<body>
<h1>Employee Details</h1>
<h2>Name: {{ data.get('name') }} </h2>
<h2>Salary: {{ data.get('salary') }}</h2>
{% if data.get('salary') >= 25000 %}
<h2>Income Tax : {{ data.get('salary') * 0.10 }}</h2>
{% else %}
<h2>Income Tax : Not applicable</h2>
{% endif %}
</body>
</html>
內容解密:
- 我們使用
{% if %}標籤來開始條件陳述式。 - 在條件表示式中,我們可以直接存取範本變數並進行比較操作。
{% else %}子句用於處理條件不成立的情況。{% endif %}標籤標誌著條件陳述式的結束。- Jinja2的條件陳述式語法與Python非常相似,但需要注意的是它使用特定的標籤來分隔邏輯和呈現層。
在範本中使用迴圈與靜態資源
範本中的迴圈結構
在 Jinja2 範本中,可以使用迴圈結構來遍歷序列物件,如列表、元組或字串。透過 for 和 endfor 關鍵字,可以實作迴圈功能。
基本語法
{% for item in sequence %}
<!-- HTML 區塊 -->
{% endfor %}
實際範例
假設我們有一個列表 langs,其中包含多種程式語言的名稱。在 main.py 中,我們將這個列表傳遞給範本:
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/profile/", response_class=HTMLResponse)
async def info(request: Request):
data = {"name": "Ronie", "langs": ["Python", "Java", "PHP", "Swift", "Ruby"]}
return templates.TemplateResponse("profile.html", {"request": request, "data": data})
在 profile.html 範本中,我們使用 for 迴圈來遍歷 langs 列表,並將每個語言名稱渲染為 HTML 的無序列表元素:
<html>
<body>
<h2>名稱:{{ data.get('name') }} </h2>
<h3>程式語言能力</h3>
<ul>
{% for lang in data.get('langs') %}
<b><li> {{ lang }}</li></b>
{% endfor %}
</ul>
</body>
</html>
輸出結果
當我們存取 http://localhost:8000/profile/ 時,瀏覽器將顯示如下內容:
名稱:Ronie
程式語言能力
• Python
• Java
• PHP
• Swift
• Ruby
提供靜態資源
Jinja2 範本引擎能夠動態替換變數資料以生成網頁。然而,為了統一顯示內容,HTML 通常會使用樣式表、圖片和 JavaScript 檔案。這些資源不隨變數資料而改變,因此被稱為網站的靜態資源。
組態靜態檔案目錄
在 FastAPI 中,我們需要將靜態檔案放在名為 static 的資料夾中。該資料夾應位於應用程式碼指令碼和 templates 資料夾所在的同一目錄下。
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")
在範本中使用靜態資源
要使用靜態資源,我們可以使用 Jinja2 的 url_for() 函式來取得資源的路徑。
使用 JavaScript 檔案
<script src="{{ url_for('static', path='myscript.js') }}"></script>
在 main.py 中定義對應的路由:
@app.get("/testjs/{name}", response_class=HTMLResponse)
async def jsdemo(request: Request, name: str):
data = {"name": name}
return templates.TemplateResponse("static-js.html", {"request": request, "data": data})
在 static-js.html 範本中,我們可以使用 JavaScript 檔案中的函式:
<!DOCTYPE html>
<html>
<head>
<title>我的網站</title>
<script src="{{ url_for('static', path='myscript.js') }}"></script>
</head>
<body>
<h2>在範本中使用 JavaScript</h2>
<h3> 歡迎 {{ data.get('name') }}</h3>
<button onclick="myFunction()">提交</button>
<p id="response"></p>
</body>
</html>
在 myscript.js 檔案中定義 myFunction():
function myFunction() {
let text;
if (confirm("您要繼續嗎?\n選擇 確定/取消") == true) {
text = "您按下了確定!";
} else {
text = "您按下了取消";
}
document.getElementById("response").innerHTML = text;
}
使用靜態圖片
要在網頁中顯示圖片,我們可以使用 <img> 標籤,並透過 url_for() 函式取得圖片路徑:
<img src="{{ url_for('static', path='fa-logo.png') }}" >
在 main.py 中定義對應的路由:
@app.get("/img/", response_class=HTMLResponse)
async def showimg(request: Request):
return templates.TemplateResponse("static-img.html", {"request": request})
在 static-img.html 範本中顯示圖片:
<!DOCTYPE html>
<html>
<head>
<title>我的網站</title>
</head>
<body>
<h2 style="text-align: center;">範本中的靜態圖片</h2>
<img src="{{ url_for('static', path='fa-logo.png') }}" >
</body>
</html>
隨著網頁技術的不斷發展,範本引擎和靜態資源管理將繼續扮演重要的角色。未來,我們可以期待更多高效的範本引擎和靜態資源管理方案,以滿足日益增長的網頁應用需求。
範本渲染流程圖示
@startuml
skinparam backgroundColor #FEFEFE
skinparam sequenceArrowThickness 2
title FastAPI 範本引擎 Jinja2 應用技巧
actor "客戶端" as client
participant "API Gateway" as gateway
participant "認證服務" as auth
participant "業務服務" as service
database "資料庫" as db
queue "訊息佇列" as mq
client -> gateway : HTTP 請求
gateway -> auth : 驗證 Token
auth --> gateway : 認證結果
alt 認證成功
gateway -> service : 轉發請求
service -> db : 查詢/更新資料
db --> service : 回傳結果
service -> mq : 發送事件
service --> gateway : 回應資料
gateway --> client : HTTP 200 OK
else 認證失敗
gateway --> client : HTTP 401 Unauthorized
end
@enduml圖表翻譯:
此圖示展示了範本渲染的基本流程。首先,使用者發起請求到指定的路由。接著,範本引擎根據提供的資料進行渲染。在渲染過程中,如果遇到列表資料,則會遍歷該列表並生成對應的 HTML 元素。最終,渲染完成的結果傳回給使用者,並在瀏覽器中顯示出來。