<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>FastAPI on YuChen</title><link>https://Dandelionlibra.github.io/tags/fastapi/</link><description>Recent content in FastAPI on YuChen</description><generator>Hugo -- gohugo.io</generator><language>zh-Hant-TW</language><lastBuildDate>Tue, 28 Apr 2026 10:46:00 +0800</lastBuildDate><atom:link href="https://Dandelionlibra.github.io/tags/fastapi/index.xml" rel="self" type="application/rss+xml"/><item><title>學 FastAPI 前你需要知道的事：HTTP 與 API 基礎</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-http-and-api/</link><pubDate>Mon, 27 Apr 2026 10:32:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-http-and-api/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e4%bb%80%e9%ba%bc%e6%98%af-api" &gt;什麼是 API？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-http-%e5%8d%94%e5%ae%9a%e8%ab%8b%e6%b1%82%e8%88%87%e5%9b%9e%e6%87%89" &gt;HTTP 協定：請求與回應&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-http-%e6%96%b9%e6%b3%95method" &gt;HTTP 方法（Method）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-http-%e7%8b%80%e6%85%8b%e7%a2%bc" &gt;HTTP 狀態碼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e4%bb%80%e9%ba%bc%e6%98%af-json" &gt;什麼是 JSON？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%be%9e%e7%80%8f%e8%a6%bd%e5%99%a8%e5%88%b0-api%e5%ae%8c%e6%95%b4%e6%b5%81%e7%a8%8b%e5%9b%9e%e9%a1%a7" &gt;從瀏覽器到 API：完整流程回顧&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-fastapi-%e7%9a%84%e5%88%86%e5%b7%a5%e6%a1%86%e6%9e%b6-vs-%e4%bc%ba%e6%9c%8d%e5%99%a8" &gt;FastAPI 的分工：框架 vs 伺服器&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-什麼是-api"&gt;1. 什麼是 API？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;API（Application Programming Interface，應用程式介面）&lt;/strong&gt; 是不同軟體之間溝通的「橋樑」。&lt;/p&gt;
&lt;p&gt;用生活中的例子來說明：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;想像去餐廳吃飯。&lt;br&gt;
&lt;strong&gt;你&lt;/strong&gt;（使用者）不需要知道廚房裡怎麼煮菜，只需要看著&lt;strong&gt;菜單&lt;/strong&gt;（API 文件），跟&lt;strong&gt;服務員&lt;/strong&gt;（API）說「我要一份炒飯」（發送請求），廚房處理好後，服務員再把菜端出來（回傳結果）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在軟體世界裡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;你的程式&lt;/strong&gt; = 客戶端（Client）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;菜單&lt;/strong&gt; = API 定義的格式與規則&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;廚房&lt;/strong&gt; = 伺服器（Server）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Web API&lt;/strong&gt; 就是透過網路（HTTP）來溝通的 API，是現在最主流的形式。手機 App 打開時載入的資料、天氣網站顯示的即時溫度，背後都是透過 Web API 取得的。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-http-協定請求與回應"&gt;2. HTTP 協定：請求與回應
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;HTTP（HyperText Transfer Protocol，超文本傳輸協定）&lt;/strong&gt; 是網際網路資料傳輸的「語言規則」，規定了客戶端與伺服器之間「怎麼說話」。&lt;/p&gt;
&lt;p&gt;HTTP 的溝通模式只有一種：&lt;strong&gt;「一問一答」&lt;/strong&gt;。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;客戶端（你的程式） ──── 請求（Request） ────▶ 伺服器
客戶端（你的程式） ◀─── 回應（Response） ───── 伺服器
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="一個-http-請求包含什麼"&gt;一個 HTTP 請求包含什麼？
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;組成部分&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;th&gt;範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;要存取的資源地址&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.example.com/users/1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;要做什麼操作&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;（取得）、&lt;code&gt;POST&lt;/code&gt;（新增）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;附加的元資訊（誰在發？帶什麼格式？）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Body&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;夾帶的資料內容（非所有請求都有）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{&amp;quot;name&amp;quot;: &amp;quot;小明&amp;quot;, &amp;quot;age&amp;quot;: 25}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="一個-http-回應包含什麼"&gt;一個 HTTP 回應包含什麼？
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;組成部分&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;th&gt;範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Status Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;請求的處理結果（成功？失敗？）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;200&lt;/code&gt;、&lt;code&gt;404&lt;/code&gt;、&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;回應的元資訊&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Body&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;回傳的資料內容&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{&amp;quot;id&amp;quot;: 1, &amp;quot;name&amp;quot;: &amp;quot;小明&amp;quot;}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id="3-http-方法method"&gt;3. HTTP 方法（Method）
&lt;/h3&gt;&lt;p&gt;HTTP 方法定義了「要對資源執行什麼動作」。這對應到資料庫操作中常見的 &lt;strong&gt;CRUD（建立、讀取、更新、刪除）&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;HTTP 方法&lt;/th&gt;
&lt;th&gt;對應 CRUD&lt;/th&gt;
&lt;th&gt;語意&lt;/th&gt;
&lt;th&gt;範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read（讀取）&lt;/td&gt;
&lt;td&gt;取得資源，不改變任何東西&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /users&lt;/code&gt; — 取得所有使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create（建立）&lt;/td&gt;
&lt;td&gt;建立一筆新資源&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /users&lt;/code&gt; — 新增一個使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update（完整更新）&lt;/td&gt;
&lt;td&gt;用新資料&lt;strong&gt;完整替換&lt;/strong&gt;一筆資源&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PUT /users/1&lt;/code&gt; — 完整更新 id=1 的使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update（部分更新）&lt;/td&gt;
&lt;td&gt;只更新資源中的&lt;strong&gt;特定欄位&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PATCH /users/1&lt;/code&gt; — 只改某個欄位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete（刪除）&lt;/td&gt;
&lt;td&gt;刪除一筆資源&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE /users/1&lt;/code&gt; — 刪除 id=1 的使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 記憶小技巧&lt;/strong&gt;：&lt;br&gt;
&lt;code&gt;GET&lt;/code&gt; = 取得（不帶 Body）&lt;br&gt;
&lt;code&gt;POST&lt;/code&gt; = 送出（帶 Body，放新資料）&lt;br&gt;
&lt;code&gt;PUT&lt;/code&gt; = 放置（完整取代）&lt;br&gt;
&lt;code&gt;PATCH&lt;/code&gt; = 修補（部分修改）&lt;br&gt;
&lt;code&gt;DELETE&lt;/code&gt; = 刪除&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="4-http-狀態碼"&gt;4. HTTP 狀態碼
&lt;/h3&gt;&lt;p&gt;狀態碼是一個三位數的數字，告訴客戶端「這次請求的結果如何」。&lt;/p&gt;
&lt;h4 id="分類規則第一個數字決定大類"&gt;分類規則（第一個數字決定大類）
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;類別&lt;/th&gt;
&lt;th&gt;範圍&lt;/th&gt;
&lt;th&gt;意義&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100–199&lt;/td&gt;
&lt;td&gt;資訊性，請求接收中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;200–299&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;成功&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;300–399&lt;/td&gt;
&lt;td&gt;重新導向&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;400–499&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;客戶端的錯誤&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500–599&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;伺服器端的錯誤&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="fastapi-開發最常見的狀態碼"&gt;FastAPI 開發最常見的狀態碼
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;狀態碼&lt;/th&gt;
&lt;th&gt;名稱&lt;/th&gt;
&lt;th&gt;使用時機&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;成功&lt;/td&gt;
&lt;td&gt;最常見，GET 成功回傳資料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;已建立&lt;/td&gt;
&lt;td&gt;POST 成功新增一筆資料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;204 No Content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;無內容&lt;/td&gt;
&lt;td&gt;成功但不回傳任何資料（常用於 DELETE）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;錯誤請求&lt;/td&gt;
&lt;td&gt;客戶端傳來的資料格式有問題&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;未授權&lt;/td&gt;
&lt;td&gt;沒有提供身份驗證（如 Token）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;403 Forbidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;禁止存取&lt;/td&gt;
&lt;td&gt;有驗證但&lt;strong&gt;沒有權限&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;找不到&lt;/td&gt;
&lt;td&gt;要存取的資源不存在&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;422 Unprocessable Entity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;無法處理&lt;/td&gt;
&lt;td&gt;資料格式正確但內容驗證失敗（FastAPI 常用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;500 Internal Server Error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;伺服器錯誤&lt;/td&gt;
&lt;td&gt;伺服器端程式碼出錯&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id="5-什麼是-json"&gt;5. 什麼是 JSON？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;JSON（JavaScript Object Notation）&lt;/strong&gt; 是目前 Web API 最主流的&lt;strong&gt;資料交換格式&lt;/strong&gt;，用來在客戶端與伺服器之間傳遞資料。&lt;/p&gt;
&lt;h4 id="json-的語法規則"&gt;JSON 的語法規則
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;王小明&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;isStudent&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;95.5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;hobbies&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;閱讀&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;寫程式&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;台北市&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;district&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;信義區&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;nickname&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JSON 規則&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;用 &lt;code&gt;{ }&lt;/code&gt; 包住物件&lt;/td&gt;
&lt;td&gt;一組 Key-Value 的集合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;用 &lt;code&gt;[ ]&lt;/code&gt; 包住陣列&lt;/td&gt;
&lt;td&gt;多筆資料的列表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key 必須用&lt;strong&gt;雙引號&lt;/strong&gt; &lt;code&gt;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;name&amp;quot;&lt;/code&gt; ✅，&lt;code&gt;name&lt;/code&gt; ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value 的類型&lt;/td&gt;
&lt;td&gt;字串、數字、布林（&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;）、陣列、物件、&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="json-與-python-dict-的對照"&gt;JSON 與 Python dict 的對照
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Python dict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;王小明&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;is_student&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# Python 用 True（大寫）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hobbies&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;閱讀&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;寫程式&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;nickname&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Python 用 None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;王小明&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;is_student&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;hobbies&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;閱讀&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;寫程式&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;nickname&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;FastAPI 會自動把 Python dict 轉換成 JSON 格式回傳，完全不需要手動處理。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="6-從瀏覽器到-api完整流程回顧"&gt;6. 從瀏覽器到 API：完整流程回顧
&lt;/h3&gt;&lt;p&gt;以開啟「天氣 App」為例，理解一次完整的 API 呼叫：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1. 使用者打開 App
↓
2. App 發送 HTTP 請求
GET https://api.weather.com/current?city=Taipei
Headers: { &amp;#34;Authorization&amp;#34;: &amp;#34;Bearer my-token&amp;#34; }
↓
3. 伺服器接收請求、查詢資料庫
↓
4. 伺服器回傳 HTTP 回應
Status: 200 OK
Body: { &amp;#34;city&amp;#34;: &amp;#34;台北&amp;#34;, &amp;#34;temperature&amp;#34;: 28, &amp;#34;humidity&amp;#34;: 75 }
↓
5. App 解析 JSON，顯示在畫面上
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id="7-fastapi-的分工框架-vs-伺服器"&gt;7. FastAPI 的分工：框架 vs 伺服器
&lt;/h3&gt;&lt;p&gt;在開始使用 FastAPI 之前，要先理解它和 Uvicorn 的分工：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;使用者的瀏覽器 / App
↓ HTTP 請求
┌─────────────┐
│ Uvicorn │ ← 負責接收網路連線、處理 HTTP 協定
└──────┬──────┘
↓ 轉交給
┌─────────────┐
│ FastAPI │ ← 負責路由分配、資料驗證、執行你的程式邏輯
└──────┬──────┘
↓ 執行
┌─────────────┐
│ 你的程式碼 │ ← def read_item(), def create_user() ...
└─────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FastAPI&lt;/strong&gt;：決定「收到這個 URL 要做什麼事」——這是你寫的邏輯&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Uvicorn&lt;/strong&gt;：負責在網路上監聽、收發 HTTP 封包——這是底層基礎設施&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：HTTP 方法配對&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：請說明以下 API 端點應該使用哪個 HTTP 方法？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;取得 id=5 的商品資訊&lt;/li&gt;
&lt;li&gt;新增一筆訂單&lt;/li&gt;
&lt;li&gt;刪除 id=3 的留言&lt;/li&gt;
&lt;li&gt;更新使用者的電子郵件（只改一個欄位）&lt;/li&gt;
&lt;li&gt;完整替換一篇文章的所有內容&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /products/5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /orders&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE /comments/3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PATCH /users/{id}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PUT /articles/{id}&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：狀態碼判讀&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：以下情境應該回傳哪個 HTTP 狀態碼？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用者輸入了帳號密碼，但帳號不存在&lt;/li&gt;
&lt;li&gt;成功建立了一筆新資料&lt;/li&gt;
&lt;li&gt;伺服器程式碼發生了 Python 例外（Exception）&lt;/li&gt;
&lt;li&gt;請求需要登入，但沒有提供 Token&lt;/li&gt;
&lt;li&gt;請求格式正確，但 email 欄位不符合 email 格式&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;404 Not Found&lt;/code&gt;（找不到此帳號）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;500 Internal Server Error&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;422 Unprocessable Entity&lt;/code&gt;（FastAPI 的資料驗證失敗）&lt;/li&gt;
&lt;/ol&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：JSON 轉 Python dict&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：將以下 JSON 轉換為 Python dict：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;is_active&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;bio&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;is_active&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# JSON true → Python True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;bio&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#75715e"&gt;# JSON null → Python None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：&lt;code&gt;true&lt;/code&gt; → &lt;code&gt;True&lt;/code&gt;，&lt;code&gt;false&lt;/code&gt; → &lt;code&gt;False&lt;/code&gt;，&lt;code&gt;null&lt;/code&gt; → &lt;code&gt;None&lt;/code&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
---
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Overview" target="_blank" rel="noopener"
&gt;MDN Web Docs - HTTP 概觀&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status" target="_blank" rel="noopener"
&gt;MDN Web Docs - HTTP 狀態碼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.mozilla.org/zh-TW/docs/Learn/JavaScript/Objects/JSON" target="_blank" rel="noopener"
&gt;MDN Web Docs - JSON 介紹&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 使用者指南&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>如何使用 FastAPI</title><link>https://Dandelionlibra.github.io/2026/04/how-to-use-fastapi/</link><pubDate>Sun, 26 Apr 2026 22:15:56 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/how-to-use-fastapi/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-fastapi-%e5%9f%ba%e6%9c%ac%e4%bb%8b%e7%b4%b9" &gt;FastAPI 基本介紹&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%ae%89%e8%a3%9d-fastapi-%e8%88%87-uvicorn" &gt;安裝 FastAPI 與 Uvicorn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%bb%ba%e7%ab%8b%e7%ac%ac%e4%b8%80%e5%80%8b-fastapi-%e6%87%89%e7%94%a8%e7%a8%8b%e5%bc%8f" &gt;建立第一個 FastAPI 應用程式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%95%9f%e5%8b%95%e4%bc%ba%e6%9c%8d%e5%99%a8%e8%88%87%e6%b8%ac%e8%a9%a6" &gt;啟動伺服器與測試&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e8%87%aa%e5%8b%95%e7%94%a2%e7%94%9f-api-%e6%96%87%e4%bb%b6" &gt;自動產生 API 文件&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-fastapi-基本介紹"&gt;1. FastAPI 基本介紹
&lt;/h3&gt;&lt;p&gt;FastAPI 是一個現代、快速（高效能）的 Web 框架，用於建構 API，基於標準 Python 類型提示。它的主要特色包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高效能&lt;/strong&gt;：可與 NodeJS 和 Go 相提並論的極高網頁效能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;易於使用&lt;/strong&gt;：自動產生互動式的 API 文件 (Swagger UI)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;減少錯誤&lt;/strong&gt;：透過資料驗證機制，減少人為錯誤。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-安裝-fastapi-與-uvicorn"&gt;2. 安裝 FastAPI 與 Uvicorn
&lt;/h3&gt;&lt;p&gt;在開始之前，請確保環境中已安裝 Python。我們需要安裝 FastAPI 框架本身，以及一個 ASGI 伺服器 (如 Uvicorn) 來運行它。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 補充說明：什麼是 Uvicorn？&lt;/strong&gt;&lt;br&gt;
FastAPI 是一個「網頁框架」，負責處理程式邏輯與路由；但它本身並沒有內建處理網路連線的伺服器功能。
因此，我們需要搭配 &lt;strong&gt;Uvicorn&lt;/strong&gt; 這個超高效能的 ASGI 伺服器。它就像是一座橋樑，負責在網路上接收使用者的 HTTP 請求，轉交給 FastAPI 處理後，再把結果傳回給瀏覽器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;請在終端機輸入以下指令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install fastapi
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install &lt;span style="color:#e6db74"&gt;&amp;#34;uvicorn[standard]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-建立第一個-fastapi-應用程式"&gt;3. 建立第一個 FastAPI 應用程式
&lt;/h3&gt;&lt;p&gt;建立一個名為 &lt;code&gt;main.py&lt;/code&gt; 的檔案，並輸入以下程式碼：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_root&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_item&lt;/span&gt;(item_id: int, q: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這段程式碼定義了兩個端點 (Endpoints)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根目錄 &lt;code&gt;/&lt;/code&gt;：回傳一個簡單的 JSON &lt;code&gt;{ &amp;quot;Hello&amp;quot;: &amp;quot;World&amp;quot; }&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/items/{item_id}&lt;/code&gt;：展示了如何接收路徑參數 (&lt;code&gt;item_id&lt;/code&gt;) 以及查詢參數 (&lt;code&gt;q&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-啟動伺服器與測試"&gt;4. 啟動伺服器與測試
&lt;/h3&gt;&lt;p&gt;在終端機中，確保處於 &lt;code&gt;main.py&lt;/code&gt; 所在的目錄，執行以下指令來啟動伺服器：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uvicorn main:app --reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt;：指的是 &lt;code&gt;main.py&lt;/code&gt; 檔案。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app&lt;/code&gt;：是 &lt;code&gt;main.py&lt;/code&gt; 中 &lt;code&gt;app = FastAPI()&lt;/code&gt; 建立的實例。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--reload&lt;/code&gt;：當程式碼變動時會自動重新載入，非常適合在開發過程中使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;啟動後，可以在瀏覽器中造訪：&lt;a class="link" href="http://127.0.0.1:8000" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000&lt;/a&gt;
將會看到：&lt;code&gt;{&amp;quot;Hello&amp;quot;: &amp;quot;World&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="5-自動產生-api-文件"&gt;5. 自動產生 API 文件
&lt;/h3&gt;&lt;p&gt;FastAPI 最大的亮點之一就是會自動產生文件。
在伺服器運行時，可以造訪：&lt;/p&gt;
&lt;h4 id="1-swagger-ui-互動式文件"&gt;1. Swagger UI (互動式文件)
&lt;/h4&gt;&lt;p&gt;造訪：&lt;a class="link" href="http://127.0.0.1:8000/docs" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000/docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何閱讀與使用 Swagger UI？&lt;/strong&gt;&lt;br&gt;
打開網頁後，會看到剛剛寫好的 &lt;code&gt;/&lt;/code&gt; 與 &lt;code&gt;/items/{item_id}&lt;/code&gt; 兩個端點 (Endpoints)：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;展開端點&lt;/strong&gt;：點擊想測試的端點（例如 &lt;code&gt;/items/{item_id}&lt;/code&gt;），它會往下展開顯示詳細資訊。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;開始測試&lt;/strong&gt;：點擊右上角的 &lt;strong&gt;「Try it out」&lt;/strong&gt; 按鈕。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;輸入參數&lt;/strong&gt;：這時參數欄位會變成可輸入狀態。可以在 &lt;code&gt;item_id&lt;/code&gt; 填入數字（例如 &lt;code&gt;5&lt;/code&gt;），在 &lt;code&gt;q&lt;/code&gt; 填入字串（例如 &lt;code&gt;somequery&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;執行請求&lt;/strong&gt;：點擊下方的 &lt;strong&gt;「Execute」&lt;/strong&gt; 藍色按鈕。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查看結果&lt;/strong&gt;：往下捲動到 &lt;strong&gt;「Responses」&lt;/strong&gt; 區塊，就可以看到伺服器回傳的真實 JSON 資料與 HTTP 狀態碼（例如 &lt;code&gt;200&lt;/code&gt; 表示成功）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="2-redoc-靜態文件"&gt;2. ReDoc (靜態文件)
&lt;/h4&gt;&lt;p&gt;造訪：&lt;a class="link" href="http://127.0.0.1:8000/redoc" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000/redoc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這是另一種風格的 API 文件。它的排版更適合當作純閱讀的參考手冊，不包含互動測試的功能。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;這樣就成功建立並運行了一個 FastAPI 應用程式！&lt;/p&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：新增一個路由&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：在 &lt;code&gt;main.py&lt;/code&gt; 中新增一個 &lt;code&gt;GET /hello/{name}&lt;/code&gt; 的端點，讓它回傳 &lt;code&gt;{&amp;quot;message&amp;quot;: &amp;quot;Hello, {name}!&amp;quot;}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;例如：呼叫 &lt;code&gt;GET /hello/Alice&lt;/code&gt; 應該回傳 &lt;code&gt;{&amp;quot;message&amp;quot;: &amp;quot;Hello, Alice!&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/hello/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;say_hello&lt;/span&gt;(name: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Hello, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：Swagger UI 操作&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：啟動伺服器後，請用 Swagger UI 測試以下兩個端點，並記錄回傳的結果：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /&lt;/code&gt; — 預期回傳什麼？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /items/42?q=fastapi&lt;/code&gt; — 預期回傳什麼？&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /&lt;/code&gt; 回傳：&lt;code&gt;{&amp;quot;Hello&amp;quot;: &amp;quot;World&amp;quot;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /items/42?q=fastapi&lt;/code&gt; 回傳：&lt;code&gt;{&amp;quot;item_id&amp;quot;: 42, &amp;quot;q&amp;quot;: &amp;quot;fastapi&amp;quot;}&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;操作步驟：開啟 &lt;a class="link" href="http://127.0.0.1:8000/docs" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000/docs&lt;/a&gt; → 點擊端點 → Try it out → 輸入參數 → Execute → 查看 Responses&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：傳入錯誤型別會怎樣？&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：在 Swagger UI 中呼叫 &lt;code&gt;GET /items/{item_id}&lt;/code&gt;，但把 &lt;code&gt;item_id&lt;/code&gt; 填入字串 &lt;code&gt;&amp;quot;abc&amp;quot;&lt;/code&gt; 而不是整數，觀察 FastAPI 回傳什麼？&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;p&gt;FastAPI 會自動回傳 &lt;code&gt;422 Unprocessable Entity&lt;/code&gt;，並在 body 中說明哪個欄位驗證失敗：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;int_parsing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be a valid integer, unable to parse string as an integer&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;abc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這就是 FastAPI 自動驗證型別的威力——完全不需要自己寫驗證邏輯！&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 使用者指南&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI 路徑參數</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-path-parameters/</link><pubDate>Mon, 27 Apr 2026 14:00:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-path-parameters/</guid><description>&lt;p&gt;在&lt;a class="link" href="https://Dandelionlibra.github.io/post/python/how-to-use-fastapi/" &gt;第一篇教學&lt;/a&gt;中，我們建立了一個簡單的 &lt;code&gt;/&lt;/code&gt; 根目錄端點。但真實世界的 API 通常需要根據不同的請求回傳不同的資料，例如：取得「特定 ID」的使用者資料。&lt;/p&gt;
&lt;p&gt;這時候，我們就需要用到&lt;strong&gt;路徑參數（Path Parameters）&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%ad%98%e4%bb%80%e9%ba%bc%e6%98%af%e5%8b%95%e6%85%8b-url" &gt;前置知識：什麼是「動態 URL」？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e4%bb%80%e9%ba%bc%e6%98%af%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8" &gt;什麼是路徑參數？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%ae%a3%e5%91%8a%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8" &gt;宣告路徑參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%8a%a0%e4%b8%8a%e5%9e%8b%e5%88%a5%e6%8f%90%e7%a4%ba%e8%87%aa%e5%8b%95%e5%9e%8b%e5%88%a5%e8%bd%89%e6%8f%9b" &gt;加上型別提示：自動型別轉換&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e8%87%aa%e5%8b%95%e8%b3%87%e6%96%99%e9%a9%97%e8%ad%89422-%e9%8c%af%e8%aa%a4" &gt;自動資料驗證（422 錯誤）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e8%b7%af%e5%be%91%e9%a0%86%e5%ba%8f%e7%9a%84%e9%87%8d%e8%a6%81%e6%80%a7" &gt;路徑順序的重要性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e4%bd%bf%e7%94%a8-enum-%e9%99%90%e5%88%b6%e5%90%88%e6%b3%95%e9%81%b8%e9%a0%85" &gt;使用 &lt;code&gt;Enum&lt;/code&gt; 限制合法選項&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#8-%e5%8c%85%e5%90%ab%e8%b7%af%e5%be%91%e5%88%86%e9%9a%94%e7%ac%a6%e7%9a%84%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8" &gt;包含路徑分隔符的路徑參數&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-前置知識什麼是動態-url"&gt;1. 前置知識：什麼是「動態 URL」？
&lt;/h3&gt;&lt;p&gt;靜態 URL 是固定的網址，例如 &lt;code&gt;/users/list&lt;/code&gt;，每次造訪都會看到一樣的東西。&lt;/p&gt;
&lt;p&gt;而&lt;strong&gt;動態 URL&lt;/strong&gt; 則允許網址的某個部分變成「變數」。例如購物網站的商品網址可能是 &lt;code&gt;/products/1&lt;/code&gt;、&lt;code&gt;/products/2&lt;/code&gt;，其中 &lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 就是動態改變的部分。後端程式會讀取這個數字，然後從資料庫撈出對應的商品資料。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-什麼是路徑參數"&gt;2. 什麼是路徑參數？
&lt;/h3&gt;&lt;p&gt;在 URL 中，那個會動態改變的部分，我們就稱為&lt;strong&gt;路徑參數&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在 FastAPI 裡，我們可以使用類似 Python &lt;code&gt;f-string&lt;/code&gt; 的語法，用大括號 &lt;code&gt;{}&lt;/code&gt; 將 URL 中的某個部分包起來，宣告它是一個變數。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="3-宣告路徑參數"&gt;3. 宣告路徑參數
&lt;/h3&gt;&lt;p&gt;讓我們在 &lt;code&gt;main.py&lt;/code&gt; 中新增一個端點，用來取得特定使用者的資料：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_user&lt;/span&gt;(user_id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個例子中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/users/{user_id}&lt;/code&gt; 告訴 FastAPI：「&lt;code&gt;/users/&lt;/code&gt; 後面的那一截字串，請當作變數 &lt;code&gt;user_id&lt;/code&gt;」。&lt;/li&gt;
&lt;li&gt;路由函式 &lt;code&gt;def read_user(user_id):&lt;/code&gt; 必須接收同名的參數 &lt;code&gt;user_id&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你在瀏覽器輸入 &lt;code&gt;http://127.0.0.1:8000/users/alice&lt;/code&gt;，就會看到回傳：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 注意：不要宣告重複的路徑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果你寫了兩個一模一樣的路由，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/test&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_1&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;Test&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/test&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_2&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;Test&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;FastAPI 的比對規則是 &lt;strong&gt;「從上往下掃描，先搶先贏」&lt;/strong&gt;。當有人造訪 &lt;code&gt;/test&lt;/code&gt; 時，任務會立刻交給第一個 &lt;code&gt;test_1&lt;/code&gt; 處理並結束。
下面的 &lt;code&gt;test_2&lt;/code&gt; 函式會變成永遠等不到客人的「幽靈程式碼」。此外，這還會導致自動產生的 API 文件（Swagger UI）發生覆蓋錯亂。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="4-加上型別提示自動型別轉換"&gt;4. 加上型別提示：自動型別轉換
&lt;/h3&gt;&lt;p&gt;在上面的例子中，&lt;code&gt;user_id&lt;/code&gt; 預設會被當作&lt;strong&gt;字串（string）&lt;/strong&gt;。但如果使用者的 ID 在資料庫中是整數（Integer）怎麼辦？&lt;/p&gt;
&lt;p&gt;在 FastAPI 中，只需要使用 Python 標準的&lt;strong&gt;型別提示（Type Hints）&lt;/strong&gt;，FastAPI 就會自動幫忙做轉換。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_item&lt;/span&gt;(item_id: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: type(item_id)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;__name__}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當你造訪 &lt;code&gt;http://127.0.0.1:8000/items/5&lt;/code&gt;，FastAPI 發現你宣告了 &lt;code&gt;item_id: int&lt;/code&gt;，就會自動把網址中的字串 &lt;code&gt;&amp;quot;5&amp;quot;&lt;/code&gt; 轉換成整數 &lt;code&gt;5&lt;/code&gt; 傳進函式中。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="5-自動資料驗證422-錯誤"&gt;5. 自動資料驗證（422 錯誤）
&lt;/h3&gt;&lt;p&gt;延續上面的例子，如果有人亂輸入網址，造訪了 &lt;code&gt;http://127.0.0.1:8000/items/foo&lt;/code&gt;，會發生什麼？字串 &lt;code&gt;&amp;quot;foo&amp;quot;&lt;/code&gt; 沒辦法變成整數啊。&lt;/p&gt;
&lt;p&gt;程式不會崩潰，FastAPI 會直接攔截這個錯誤，並回傳清楚的 HTTP 錯誤狀態碼 &lt;strong&gt;422 Unprocessable Entity&lt;/strong&gt;（無法處理的實體），並附帶錯誤說明：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;int_parsing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be a valid integer, unable to parse string as an integer&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;錯誤訊息明確指出：在路徑（path）中的 &lt;code&gt;item_id&lt;/code&gt; 發生了轉換整數失敗的錯誤。這省去了我們自己寫 &lt;code&gt;if not item_id.isdigit(): ...&lt;/code&gt; 等檢查邏輯。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="6-路徑順序的重要性"&gt;6. 路徑順序的重要性
&lt;/h3&gt;&lt;p&gt;在設計 API 時，有時候會同時有「固定路徑」和「動態路徑」。&lt;/p&gt;
&lt;p&gt;例如，我們有一個取得所有使用者的端點 &lt;code&gt;/users/all&lt;/code&gt;，也有一個取得特定使用者的端點 &lt;code&gt;/users/{user_id}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在 FastAPI 中，&lt;strong&gt;路由的宣告順序非常重要&lt;/strong&gt;。程式碼是從上往下執行的，FastAPI 會比對第一個符合條件的路徑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;錯誤的寫法：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_user&lt;/span&gt;(user_id: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/all&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_all_users&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果這樣寫，當你造訪 &lt;code&gt;/users/all&lt;/code&gt; 時，FastAPI 會先看到 &lt;code&gt;@app.get(&amp;quot;/users/{user_id}&amp;quot;)&lt;/code&gt;，然後把 &lt;code&gt;&amp;quot;all&amp;quot;&lt;/code&gt; 當作是變數 &lt;code&gt;user_id&lt;/code&gt; 的值傳進去，這是錯誤的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正確的寫法（固定路徑在前）：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/all&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_all_users&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_user&lt;/span&gt;(user_id: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把固定的路由寫在前面，就能確保它會被正確攔截。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="7-使用-enum-限制合法選項"&gt;7. 使用 &lt;code&gt;Enum&lt;/code&gt; 限制合法選項
&lt;/h3&gt;&lt;p&gt;有時候，路徑參數不能隨便亂填，只能是幾個特定的選項。這時候可以使用 Python 內建的 &lt;code&gt;Enum&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;假設我們有一個機器學習模型 API，只接受 &lt;code&gt;alexnet&lt;/code&gt;、&lt;code&gt;resnet&lt;/code&gt;、&lt;code&gt;lenet&lt;/code&gt; 三種模型名稱：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; enum &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Enum
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ModelName&lt;/span&gt;(str, Enum):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alexnet &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;alexnet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resnet &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;resnet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lenet &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;lenet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/models/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{model_name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_model&lt;/span&gt;(model_name: ModelName):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; model_name &lt;span style="color:#f92672"&gt;==&lt;/span&gt; ModelName&lt;span style="color:#f92672"&gt;.&lt;/span&gt;alexnet:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;: model_name, &lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Deep Learning FTW!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; model_name&lt;span style="color:#f92672"&gt;.&lt;/span&gt;value &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;lenet&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;: model_name, &lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;LeCNN all the masses&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;: model_name, &lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Have some residuals&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;這個寫法有三個好處：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;編輯器支援&lt;/strong&gt;：打字時會有自動補全（Auto-completion）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自動驗證&lt;/strong&gt;：如果使用者輸入 &lt;code&gt;http://127.0.0.1:8000/models/yolov4&lt;/code&gt;，FastAPI 會直接回傳 422 錯誤，並告訴使用者有哪些合法的選項。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;enum&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be &amp;#39;alexnet&amp;#39;, &amp;#39;resnet&amp;#39; or &amp;#39;lenet&amp;#39;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;yolov4&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;ctx&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;expected&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;alexnet&amp;#39;, &amp;#39;resnet&amp;#39; or &amp;#39;lenet&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;API 文件&lt;/strong&gt;：Swagger UI 會自動把這個欄位變成一個&lt;strong&gt;下拉式選單&lt;/strong&gt;，讓測試更方便！&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="8-包含路徑分隔符的路徑參數"&gt;8. 包含路徑分隔符的路徑參數
&lt;/h3&gt;&lt;p&gt;最後一個比較特殊的情境是：如果路徑參數本身就是一個檔案路徑（包含 &lt;code&gt;/&lt;/code&gt;）怎麼辦？&lt;/p&gt;
&lt;p&gt;例如 URL 長這樣：&lt;code&gt;/files/home/johndoe/myfile.txt&lt;/code&gt;。
我們希望 &lt;code&gt;/files/&lt;/code&gt; 後面的所有東西 &lt;code&gt;home/johndoe/myfile.txt&lt;/code&gt; 都被當成一個變數。&lt;/p&gt;
&lt;p&gt;FastAPI 支援 Starlette 的特殊路徑語法：&lt;code&gt;:path&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/files/{file_path:path}&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_file&lt;/span&gt;(file_path: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;file_path&amp;#34;&lt;/span&gt;: file_path}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只要加上 &lt;code&gt;:path&lt;/code&gt;，FastAPI 就不會因為遇到 &lt;code&gt;/&lt;/code&gt; 就把字串切斷，而是會把它一字不漏地全部抓下來。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試範例：&lt;/strong&gt;
如果在瀏覽器輸入 &lt;code&gt;http://127.0.0.1:8000/files/home/myfile.txt&lt;/code&gt;，將會收到完整的路徑字串：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;file_path&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;home/myfile.txt&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：撰寫產品分類 API&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 API 端點 &lt;code&gt;GET /categories/{category_name}&lt;/code&gt;。
這個端點應該接收字串型別的 &lt;code&gt;category_name&lt;/code&gt;，並回傳 JSON 格式的分類名稱。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/categories/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{category_name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_category&lt;/span&gt;(category_name: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;category&amp;#34;&lt;/span&gt;: category_name}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：修復順序錯誤&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：以下的程式碼有個問題。如果造訪 &lt;code&gt;/posts/latest&lt;/code&gt; 會發生什麼事？請將程式碼修改成正確的順序。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{post_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_post&lt;/span&gt;(post_id: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post_id&amp;#34;&lt;/span&gt;: post_id}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/latest&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_latest_post&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;This is the latest post!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;p&gt;如果照著原本的寫法，造訪 &lt;code&gt;/posts/latest&lt;/code&gt; 時，FastAPI 會以為 &lt;code&gt;&amp;quot;latest&amp;quot;&lt;/code&gt; 是 &lt;code&gt;post_id&lt;/code&gt;。由於 &lt;code&gt;post_id&lt;/code&gt; 規定要是整數（&lt;code&gt;int&lt;/code&gt;），所以會丟出 &lt;strong&gt;422 Validation Error&lt;/strong&gt; 錯誤。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正確寫法&lt;/strong&gt;（把固定路徑移到上面）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/latest&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_latest_post&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;This is the latest post!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{post_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_post&lt;/span&gt;(post_id: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post_id&amp;#34;&lt;/span&gt;: post_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：使用 Enum 限制顏色&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：建立一個 &lt;code&gt;GET /items/color/{color_name}&lt;/code&gt; 的端點。
請使用 &lt;code&gt;Enum&lt;/code&gt; 限制 &lt;code&gt;color_name&lt;/code&gt; 只能是 &lt;code&gt;red&lt;/code&gt;、&lt;code&gt;green&lt;/code&gt; 或 &lt;code&gt;blue&lt;/code&gt; 其中一種。如果輸入合法，回傳對應的顏色名稱。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; enum &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Enum
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ColorName&lt;/span&gt;(str, Enum):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; red &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;red&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; green &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;green&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; blue &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blue&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/color/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{color_name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_color&lt;/span&gt;(color_name: ColorName):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;selected_color&amp;#34;&lt;/span&gt;: color_name}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/path-params/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 路徑參數&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI 查詢參數</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-query-parameters/</link><pubDate>Mon, 27 Apr 2026 18:55:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-query-parameters/</guid><description>&lt;p&gt;在上一篇，我們學習了如何使用「路徑參數」來建立動態的 URL，例如透過 &lt;code&gt;/users/alice&lt;/code&gt; 來取得特定使用者的資料。
然而，如果我們想要在一個網頁中加入**「搜尋關鍵字」&lt;strong&gt;、&lt;/strong&gt;「分頁（第幾頁）」&lt;strong&gt;或是&lt;/strong&gt;「篩選條件（價格由高到低）」**，把這些條件全部塞進斜線 &lt;code&gt;/&lt;/code&gt; 裡面會變得非常難以管理。&lt;/p&gt;
&lt;p&gt;這時候，需要用到另一種常見的傳值方式：&lt;strong&gt;查詢參數（Query Parameters）&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e4%bb%80%e9%ba%bc%e6%98%af%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;什麼是查詢參數？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%ae%a3%e5%91%8a%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;宣告查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e8%a8%ad%e5%ae%9a%e9%a0%90%e8%a8%ad%e5%80%bc%e9%81%b8%e5%a1%ab%e5%8f%83%e6%95%b8" &gt;設定預設值（選填參數）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%bf%85%e5%a1%ab%e7%9a%84%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;必填的查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e4%bd%bf%e7%94%a8-optional-%e8%a1%a8%e7%a4%ba%e5%8f%af%e9%81%b8%e6%ac%84%e4%bd%8d" &gt;使用 Optional 表示可選欄位&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%b8%83%e6%9e%97%e5%80%bc-bool-%e7%9a%84%e8%87%aa%e5%8b%95%e8%bd%89%e6%8f%9b" &gt;布林值 (bool) 的自動轉換&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e5%90%8c%e6%99%82%e4%bd%bf%e7%94%a8%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8%e8%88%87%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;同時使用路徑參數與查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-什麼是查詢參數"&gt;1. 什麼是查詢參數？
&lt;/h3&gt;&lt;p&gt;大家一定有用過 Google 搜尋。當你在 Google 搜尋「FastAPI」時，你會發現網址列變成這樣：
&lt;code&gt;https://www.google.com/search?q=FastAPI&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在這裡，&lt;code&gt;?&lt;/code&gt; 後面的字串就是&lt;strong&gt;查詢參數&lt;/strong&gt;。它的語法規則非常簡單：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以問號 &lt;code&gt;?&lt;/code&gt; 作為開頭。&lt;/li&gt;
&lt;li&gt;格式是 &lt;code&gt;鍵=值&lt;/code&gt;（Key=Value），例如 &lt;code&gt;q=FastAPI&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果有多個參數，就用 &lt;code&gt;&amp;amp;&lt;/code&gt; 符號串接起來，例如 &lt;code&gt;?q=FastAPI&amp;amp;page=2&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="2-宣告查詢參數"&gt;2. 宣告查詢參數
&lt;/h3&gt;&lt;p&gt;在 FastAPI 中，要宣告查詢參數非常簡單。&lt;strong&gt;只要在路由函式中加入的參數名稱，沒有出現在路徑 &lt;code&gt;@app.get(...)&lt;/code&gt; 的 &lt;code&gt;{}&lt;/code&gt; 裡面，FastAPI 就會自動把它當作查詢參數！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以下是簡單的分頁 API 範例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_items&lt;/span&gt;(skip: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, limit: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: skip, &lt;span style="color:#e6db74"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: limit}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個例子中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路徑是 &lt;code&gt;/items/&lt;/code&gt;，裡面&lt;strong&gt;沒有&lt;/strong&gt;任何 &lt;code&gt;{}&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;函式 &lt;code&gt;read_items&lt;/code&gt; 卻接收了 &lt;code&gt;skip&lt;/code&gt; 和 &lt;code&gt;limit&lt;/code&gt; 兩個參數。&lt;/li&gt;
&lt;li&gt;因此，FastAPI 會自動認定 &lt;code&gt;skip&lt;/code&gt; 和 &lt;code&gt;limit&lt;/code&gt; 是從網址 &lt;code&gt;?&lt;/code&gt; 後面傳進來的查詢參數。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="3-設定預設值選填參數"&gt;3. 設定預設值（選填參數）
&lt;/h3&gt;&lt;p&gt;上面的範例中，我們給了參數預設值：&lt;code&gt;skip: int = 0&lt;/code&gt; 和 &lt;code&gt;limit: int = 10&lt;/code&gt;。
這代表這兩個參數是&lt;strong&gt;選填的（Optional）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果你直接造訪 &lt;code&gt;http://127.0.0.1:8000/items/&lt;/code&gt;：
由於你沒有在網址提供參數，FastAPI 會使用你設定的預設值，回傳：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;如果你造訪 &lt;code&gt;http://127.0.0.1:8000/items/?skip=20&lt;/code&gt;：
FastAPI 會讀取網址的 &lt;code&gt;skip&lt;/code&gt;，而 &lt;code&gt;limit&lt;/code&gt; 依然使用預設值：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;如果你造訪 &lt;code&gt;http://127.0.0.1:8000/items/?skip=20&amp;amp;limit=5&lt;/code&gt;：
FastAPI 會同時讀取網址的 &lt;code&gt;skip&lt;/code&gt; 和 &lt;code&gt;limit&lt;/code&gt;：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="4-必填的查詢參數"&gt;4. 必填的查詢參數
&lt;/h3&gt;&lt;p&gt;如果我們希望某個查詢參數是**必填（Required）**的，該怎麼做？
很簡單，&lt;strong&gt;不要給它預設值就好了！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;假設我們正在寫一個搜尋 API，使用者一定要提供搜尋的關鍵字 &lt;code&gt;q&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_items&lt;/span&gt;(q: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;search_keyword&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因為 &lt;code&gt;q&lt;/code&gt; 沒有等號 &lt;code&gt;=&lt;/code&gt; 賦予預設值，它就變成了必填參數。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;
如果你沒有提供 &lt;code&gt;q&lt;/code&gt;，直接造訪 &lt;code&gt;http://127.0.0.1:8000/search&lt;/code&gt;，FastAPI 會非常貼心地自動擋下來，並回傳 422 錯誤，告訴客戶端少傳了資料：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;missing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;query&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Field required&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;必須輸入 &lt;code&gt;http://127.0.0.1:8000/search?q=apple&lt;/code&gt; 才能成功取得結果。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="5-使用-optional-表示可選欄位"&gt;5. 使用 Optional 表示可選欄位
&lt;/h3&gt;&lt;p&gt;有時候，我們希望一個參數是選填的，但在使用者沒有提供時，我們希望它的值是 &lt;code&gt;None&lt;/code&gt;（空值），而不是某個具體的數字或字串。&lt;/p&gt;
&lt;p&gt;這時候建議搭配 Python &lt;code&gt;typing&lt;/code&gt; 模組中的 &lt;code&gt;Optional&lt;/code&gt; 來寫，這可以讓編輯器的提示更準確，程式碼也更有可讀性：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/products&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_products&lt;/span&gt;(category: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; category:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;顯示 &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;category&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 類別的產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;顯示所有產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的 &lt;code&gt;Optional[str] = None&lt;/code&gt; 告訴了任何人（包含編輯器與 FastAPI）：「這個參數是字串，但它是可選的，如果不填，預設就是 None」。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果造訪 &lt;code&gt;http://127.0.0.1:8000/products&lt;/code&gt;（不提供參數）：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;顯示所有產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;如果造訪 &lt;code&gt;http://127.0.0.1:8000/products?category=3C&lt;/code&gt;（提供參數）：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;顯示 3C 類別的產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="6-布林值-bool-的自動轉換"&gt;6. 布林值 (bool) 的自動轉換
&lt;/h3&gt;&lt;p&gt;FastAPI 的資料轉換機制非常聰明，尤其是對於布林值（Boolean）。&lt;/p&gt;
&lt;p&gt;如果宣告了一個布林型別的查詢參數：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/active&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_active_users&lt;/span&gt;(short_format: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; short_format:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [{&lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;}, {&lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;}]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當你要在網址中把 &lt;code&gt;short_format&lt;/code&gt; 設為 &lt;code&gt;True&lt;/code&gt; 時，你不用只打 &lt;code&gt;true&lt;/code&gt;。FastAPI 會自動識別以下所有字眼，並把它們轉換成 Python 的 &lt;code&gt;True&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;?short_format=true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=True&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=on&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=yes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相反地，&lt;code&gt;false&lt;/code&gt;、&lt;code&gt;0&lt;/code&gt;、&lt;code&gt;off&lt;/code&gt;、&lt;code&gt;no&lt;/code&gt; 都會被轉換為 Python 的 &lt;code&gt;False&lt;/code&gt;。這對前端工程師來說是一個極度友善的設計！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;造訪 &lt;code&gt;http://127.0.0.1:8000/users/active?short_format=1&lt;/code&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;造訪 &lt;code&gt;http://127.0.0.1:8000/users/active?short_format=0&lt;/code&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;,&lt;span style="color:#f92672"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;,&lt;span style="color:#f92672"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="7-同時使用路徑參數與查詢參數"&gt;7. 同時使用路徑參數與查詢參數
&lt;/h3&gt;&lt;p&gt;在實務上，最常把「路徑參數」和「查詢參數」混在一起使用。
例如：若想查看「特定使用者（路徑）」底下的「某些物品，並進行分頁（查詢）」。&lt;/p&gt;
&lt;p&gt;不用擔心順序問題，FastAPI 會根據變數名稱自動幫你分類：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/items&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_user_items&lt;/span&gt;(user_id: int, short: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;, skip: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; item_data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id, &lt;span style="color:#e6db74"&gt;&amp;#34;items&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;筆電&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;滑鼠&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; short:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id, &lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; item_data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個例子中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_id&lt;/code&gt; 因為出現在 &lt;code&gt;@app.get&lt;/code&gt; 的 &lt;code&gt;{}&lt;/code&gt; 中，所以它是&lt;strong&gt;路徑參數&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;short&lt;/code&gt; 和 &lt;code&gt;skip&lt;/code&gt; 沒有出現在路徑中，所以它們是&lt;strong&gt;查詢參數&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果造訪：&lt;code&gt;http://127.0.0.1:8000/users/5/items?short=yes&amp;amp;skip=1&lt;/code&gt;
FastAPI 就能將 &lt;code&gt;user_id&lt;/code&gt; 解析為 &lt;code&gt;5&lt;/code&gt;，&lt;code&gt;short&lt;/code&gt; 解析為 &lt;code&gt;True&lt;/code&gt;，&lt;code&gt;skip&lt;/code&gt; 解析為 &lt;code&gt;1&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：建立排序 API&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 API 端點 &lt;code&gt;GET /books&lt;/code&gt;。
該端點需要接收一個查詢參數 &lt;code&gt;sort_by&lt;/code&gt;。如果使用者沒有提供，預設應該是 &lt;code&gt;&amp;quot;id&amp;quot;&lt;/code&gt;。如果使用者提供，就回傳使用者輸入的排序方式。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/books&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_books&lt;/span&gt;(sort_by: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;sort_by&amp;#34;&lt;/span&gt;: sort_by}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：必填與選填混搭&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 API 端點 &lt;code&gt;GET /search/videos&lt;/code&gt;。
該端點需要一個&lt;strong&gt;必填&lt;/strong&gt;的查詢參數 &lt;code&gt;keyword&lt;/code&gt;（字串），以及一個&lt;strong&gt;選填&lt;/strong&gt;的參數 &lt;code&gt;max_results&lt;/code&gt;（整數，預設為 50）。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search/videos&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_videos&lt;/span&gt;(keyword: str, max_results: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;keyword&amp;#34;&lt;/span&gt;: keyword, &lt;span style="color:#e6db74"&gt;&amp;#34;max_results&amp;#34;&lt;/span&gt;: max_results}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：如何判斷參數類型？&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：觀察以下的程式碼，請問 &lt;code&gt;model_id&lt;/code&gt; 和 &lt;code&gt;version&lt;/code&gt; 分別是什麼類型的參數？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/models/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{model_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;check_model&lt;/span&gt;(model_id: int, version: float):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;model_id&lt;/code&gt; 是&lt;strong&gt;路徑參數&lt;/strong&gt;，因為它被宣告在 &lt;code&gt;@app.get&lt;/code&gt; 的路徑字串 &lt;code&gt;{}&lt;/code&gt; 中。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;version&lt;/code&gt; 是&lt;strong&gt;查詢參數&lt;/strong&gt;，因為它沒有出現在路徑字串中，而且它是必填的（沒有預設值）。造訪時的網址會像這樣：&lt;code&gt;/models/10?version=1.5&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/query-params/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI Request Body</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-request-body/</link><pubDate>Mon, 27 Apr 2026 23:21:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-request-body/</guid><description>&lt;p&gt;在前幾篇文章中，我們學會了如何透過「網址（URL）」把資料傳給伺服器，例如：路徑參數 &lt;code&gt;/users/123&lt;/code&gt; 或是查詢參數 &lt;code&gt;?skip=10&lt;/code&gt;。
但如果要傳送&lt;strong&gt;大量資料&lt;/strong&gt;（例如：一整篇部落格文章的內容）或是&lt;strong&gt;機密資料&lt;/strong&gt;（例如：登入的帳號密碼），把它們全部塞進網址裡就不太適合了。這時候需要使用 &lt;strong&gt;Request Body（請求本文）&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%ad%98http-%e6%96%b9%e6%b3%95%e8%88%87-pydantic" &gt;前置知識：HTTP 方法與 Pydantic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e4%bb%80%e9%ba%bc%e6%98%af-request-body" &gt;什麼是 Request Body？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%bb%ba%e7%ab%8b%e7%ac%ac%e4%b8%80%e5%80%8b-pydantic-%e6%a8%a1%e5%9e%8b" &gt;建立第一個 Pydantic 模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%9c%a8%e8%b7%af%e7%94%b1%e5%87%bd%e5%bc%8f%e4%b8%ad%e5%ae%a3%e5%91%8a-body-%e5%8f%83%e6%95%b8" &gt;在路由函式中宣告 Body 參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e9%81%b8%e5%a1%ab%e6%ac%84%e4%bd%8d%e8%88%87%e9%a0%90%e8%a8%ad%e5%80%bc%e8%a8%ad%e5%ae%9a" &gt;選填欄位與預設值設定&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%9c%a8%e5%87%bd%e5%bc%8f%e5%85%a7%e9%83%a8%e4%bd%bf%e7%94%a8%e6%a8%a1%e5%9e%8b%e8%b3%87%e6%96%99" &gt;在函式內部使用模型資料&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e4%b8%89%e5%90%88%e4%b8%80%e6%a8%a1%e5%9e%8b--%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8--%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;三合一：模型 + 路徑參數 + 查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#8-pydantic-%e7%9a%84%e8%87%aa%e5%8b%95%e9%a9%97%e8%ad%89%e6%a9%9f%e5%88%b6" &gt;Pydantic 的自動驗證機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-前置知識http-方法與-pydantic"&gt;1. 前置知識：HTTP 方法與 Pydantic
&lt;/h3&gt;&lt;p&gt;在開始實作之前，需要先了解三個重要觀念：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;為什麼要用 POST？&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;GET&lt;/code&gt; 請求是用來「取得」資料，傳統上不會夾帶 Body。如果要傳送資料給伺服器來「新增」或「修改」東西，通常會使用 &lt;code&gt;POST&lt;/code&gt;、&lt;code&gt;PUT&lt;/code&gt; 或 &lt;code&gt;PATCH&lt;/code&gt; 這些 HTTP 方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;什麼是 Pydantic？&lt;/strong&gt;&lt;br&gt;
FastAPI 的強大，有一半歸功於 &lt;strong&gt;Pydantic&lt;/strong&gt;。Pydantic 是一個 Python 的資料驗證庫，它的核心概念是：&lt;strong&gt;如果資料驗證通過了，拿到的一定是預期的型別&lt;/strong&gt;；如果驗證失敗，它會直接拋出清楚的錯誤，不會讓錯誤的資料跑進系統。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;什麼是 &lt;code&gt;class&lt;/code&gt; 繼承？&lt;/strong&gt;&lt;br&gt;
在 Python 裡，&lt;code&gt;class&lt;/code&gt;（類別）用來定義一個物件的藍圖。當我們寫 &lt;code&gt;class Item(BaseModel):&lt;/code&gt; 時，代表 &lt;code&gt;Item&lt;/code&gt; 這個模型「繼承」了 Pydantic 提供的 &lt;code&gt;BaseModel&lt;/code&gt;，這讓 &lt;code&gt;Item&lt;/code&gt; 瞬間擁有了自動轉換 JSON、檢查型別的超能力！&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="2-什麼是-request-body"&gt;2. 什麼是 Request Body？
&lt;/h3&gt;&lt;p&gt;Request Body（請求本文）是指客戶端（如瀏覽器、手機 App）在發送 HTTP 請求時，夾帶在請求底層的資料，最常見的格式就是 &lt;strong&gt;JSON&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;例如，當想在購物車裡新增一個商品時，客戶端會送出這樣一段 JSON：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;滑鼠&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;999.0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;is_offer&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="3-建立第一個-pydantic-模型"&gt;3. 建立第一個 Pydantic 模型
&lt;/h3&gt;&lt;p&gt;在 FastAPI 中，要接收並處理上面的 JSON 資料，第一步就是利用 Pydantic 的 &lt;code&gt;BaseModel&lt;/code&gt; 來宣告資料結構。&lt;/p&gt;
&lt;p&gt;我們可以在檔案的最上方，把需要的工具匯入進來，然後定義一個 &lt;code&gt;Item&lt;/code&gt; 模型：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; is_offer: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的寫法與標準的 Python 變數宣告一模一樣：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; 必須是字串 (&lt;code&gt;str&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;price&lt;/code&gt; 必須是浮點數 (&lt;code&gt;float&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is_offer&lt;/code&gt; 是一個布林值 (&lt;code&gt;bool&lt;/code&gt;)，並且有一個預設值 &lt;code&gt;None&lt;/code&gt;（代表如果前端沒傳這個欄位也沒關係）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="4-在路由函式中宣告-body-參數"&gt;4. 在路由函式中宣告 Body 參數
&lt;/h3&gt;&lt;p&gt;有了模型之後，只要把它當作參數，加進路由函式裡就可以了！&lt;/p&gt;
&lt;p&gt;請注意，這次要用的是 &lt;code&gt;@app.post&lt;/code&gt;，因為是要「新增」資料：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_item&lt;/span&gt;(item: Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當 FastAPI 看到 &lt;code&gt;item: Item&lt;/code&gt; 時：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;它知道 &lt;code&gt;Item&lt;/code&gt; 是一個繼承自 &lt;code&gt;BaseModel&lt;/code&gt; 的類別。&lt;/li&gt;
&lt;li&gt;根據「路徑 vs 查詢」的規則，&lt;code&gt;item&lt;/code&gt; 既不在路徑 &lt;code&gt;{}&lt;/code&gt; 裡，也不是簡單的型別（如 &lt;code&gt;int&lt;/code&gt; 或 &lt;code&gt;str&lt;/code&gt;），所以 FastAPI 會自動把它判定為 &lt;strong&gt;Request Body&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;
啟動伺服器後，到 Swagger UI (&lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;) 找到 &lt;code&gt;POST /items/&lt;/code&gt;，點擊「Try it out」，你會發現 FastAPI 已經幫你準備好了一個可以填寫的 JSON 框，輸入資料後送出，就會原封不動地回傳你填入的資料。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="5-選填欄位與預設值設定"&gt;5. 選填欄位與預設值設定
&lt;/h3&gt;&lt;p&gt;和查詢參數一樣，可以使用 Python 的 &lt;code&gt;Optional&lt;/code&gt; 來讓模型的欄位意圖更清晰：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tax: Optional[float] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個模型中，&lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;price&lt;/code&gt; 是&lt;strong&gt;必填的&lt;/strong&gt;。而 &lt;code&gt;description&lt;/code&gt; 和 &lt;code&gt;tax&lt;/code&gt; 是&lt;strong&gt;選填的&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="6-在函式內部使用模型資料"&gt;6. 在函式內部使用模型資料
&lt;/h3&gt;&lt;p&gt;當 FastAPI 把 JSON 自動轉換為 Pydantic 模型（例如上方的 &lt;code&gt;item&lt;/code&gt; 變數）後，在函式內部就可以直接用「點 (&lt;code&gt;.&lt;/code&gt;)」來存取屬性，而且還有編輯器的自動補全功能！&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_item&lt;/span&gt;(item: Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 可以直接用 item.price，不需要像字典一樣寫成 item[&amp;#34;price&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total_price &lt;span style="color:#f92672"&gt;=&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;price
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tax:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total_price &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tax
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_name&amp;#34;&lt;/span&gt;: item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name, &lt;span style="color:#e6db74"&gt;&amp;#34;total_price&amp;#34;&lt;/span&gt;: total_price}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這在開發時非常舒服，因為再也不用擔心把欄位名稱打錯，編輯器都會幫忙檢查。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="7-三合一模型--路徑參數--查詢參數"&gt;7. 三合一：模型 + 路徑參數 + 查詢參數
&lt;/h3&gt;&lt;p&gt;FastAPI 最厲害的地方在於，可以毫無負擔地把三種參數全部混在一起用，而且依然能清晰地分類。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.put&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_item&lt;/span&gt;(item_id: int, item: Item, q: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; q:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update({&lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: q})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個端點中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;item_id&lt;/code&gt;：有出現在路徑 &lt;code&gt;/items/{item_id}&lt;/code&gt; 裡，所以是&lt;strong&gt;路徑參數&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt;：沒有出現在路徑裡，且型別是簡單的字串，所以是&lt;strong&gt;查詢參數&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;item&lt;/code&gt;：型別是 Pydantic 的 &lt;code&gt;Item&lt;/code&gt; 模型，所以是 &lt;strong&gt;Request Body&lt;/strong&gt;。
ㄋ&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="8-pydantic-的自動驗證機制"&gt;8. Pydantic 的自動驗證機制
&lt;/h3&gt;&lt;p&gt;如果客戶端傳來了錯誤型別的資料怎麼辦？例如，&lt;code&gt;price&lt;/code&gt; 應該要是數字，前端卻傳了一個字串：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;滑鼠&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;三百塊&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不用擔心程式崩潰，因為有 Pydantic 把關，FastAPI 會直接攔截下來，並回傳 422 錯誤告訴使用者：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;float_parsing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be a valid number, unable to parse string as a number&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;三百塊&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意看這裡的 &lt;code&gt;loc&lt;/code&gt;，它會明確指出錯誤發生在 &lt;code&gt;&amp;quot;body&amp;quot;&lt;/code&gt;（Request Body）裡的 &lt;code&gt;&amp;quot;price&amp;quot;&lt;/code&gt; 欄位，這大大節省除錯的時間。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：建立使用者模型&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：請利用 Pydantic 建立一個名為 &lt;code&gt;User&lt;/code&gt; 的模型。
這個模型需要有以下三個欄位：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;username&lt;/code&gt;：字串（必填）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;email&lt;/code&gt;：字串（必填）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;age&lt;/code&gt;：整數（選填，預設為 18）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; email: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; age: Optional[int] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：宣告 POST 路由&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：接續上一題，請撰寫一個 API 端點 &lt;code&gt;POST /users/&lt;/code&gt;，並使用剛才建立的 &lt;code&gt;User&lt;/code&gt; 模型作為 Request Body。
在函式內部，請回傳一個包含 &lt;code&gt;message&lt;/code&gt; 和使用者名稱的 JSON，例如：&lt;code&gt;{&amp;quot;message&amp;quot;: &amp;quot;成功建立使用者：Alice&amp;quot;}&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_user&lt;/span&gt;(user: User):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;成功建立使用者：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;user&lt;span style="color:#f92672"&gt;.&lt;/span&gt;username&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：參數分類大哉問&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：觀察以下的程式碼，請問 &lt;code&gt;user_id&lt;/code&gt;、&lt;code&gt;token&lt;/code&gt; 和 &lt;code&gt;profile&lt;/code&gt; 分別屬於哪一種參數（路徑、查詢、還是 Body）？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.patch&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/profile/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_profile&lt;/span&gt;(user_id: int, profile: User, token: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_id&lt;/code&gt; 是&lt;strong&gt;路徑參數&lt;/strong&gt;（因為出現在路徑的 &lt;code&gt;{}&lt;/code&gt; 中）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;profile&lt;/code&gt; 是 &lt;strong&gt;Request Body&lt;/strong&gt;（因為型別為 Pydantic 模型 &lt;code&gt;User&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;token&lt;/code&gt; 是&lt;strong&gt;查詢參數&lt;/strong&gt;（沒有在路徑中，且是基本字串型別）。網址會長得像 &lt;code&gt;/profile/5?token=abc&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 請求本文&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI Body 進階：多個模型、巢狀結構與欄位驗證</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-body-advanced/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-body-advanced/</guid><description>&lt;p&gt;上一篇介紹了如何使用 Pydantic 建立基本的 Request Body 模型。然而，真實世界的 API 往往會面臨更複雜的需求：
如果想在同一個請求中同時接收「商品資料」和「使用者資料」該怎麼辦？如果模型裡面還包含著另一個模型（巢狀結構）呢？&lt;/p&gt;
&lt;p&gt;這篇將解鎖 FastAPI 與 Pydantic 的進階用法，讓 API 驗證變得更靈活。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%90%8c%e6%99%82%e6%8e%a5%e6%94%b6%e5%a4%9a%e5%80%8b-body-%e5%8f%83%e6%95%b8" &gt;同時接收多個 Body 參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e4%bd%bf%e7%94%a8-body-%e6%98%8e%e7%a2%ba%e5%ae%a3%e5%91%8a%e5%96%ae%e4%b8%80%e5%80%bc" &gt;使用 Body() 明確宣告單一值&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e4%bd%bf%e7%94%a8-field-%e7%82%ba%e6%ac%84%e4%bd%8d%e5%8a%a0%e4%b8%8a%e9%a9%97%e8%ad%89" &gt;使用 Field() 為欄位加上驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-list-%e8%88%87-set%e8%99%95%e7%90%86%e9%99%a3%e5%88%97%e8%b3%87%e6%96%99" &gt;List 與 Set：處理陣列資料&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e5%b7%a2%e7%8b%80%e6%a8%a1%e5%9e%8b%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84%e6%a8%a1%e5%9e%8b" &gt;巢狀模型：模型中的模型&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-同時接收多個-body-參數"&gt;1. 同時接收多個 Body 參數
&lt;/h3&gt;&lt;p&gt;如果想在同一個路由中接收兩種不同的資料結構，例如：建立一筆訂單時，同時需要 &lt;code&gt;Item&lt;/code&gt; 商品資訊與 &lt;code&gt;User&lt;/code&gt; 買家資訊，直接把它們都宣告為參數即可，FastAPI 會自動將它們視為 Request Body。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; full_name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.put&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/orders/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{order_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_order&lt;/span&gt;(order_id: int, item: Item, user: User):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: order_id, &lt;span style="color:#e6db74"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: user}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當 FastAPI 看到兩個繼承自 &lt;code&gt;BaseModel&lt;/code&gt; 的參數時，它會預期客戶端傳來的 JSON 長這樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;機械鍵盤&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3000.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice123&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;full_name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Alice Wang&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它會自動使用參數名稱（&lt;code&gt;item&lt;/code&gt; 和 &lt;code&gt;user&lt;/code&gt;）作為 JSON 最外層的 Key，並把資料完美解析出來。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-使用-body-明確宣告單一值"&gt;2. 使用 Body() 明確宣告單一值
&lt;/h3&gt;&lt;p&gt;有時候，除了完整的 Pydantic 模型之外，可能還想額外接收一個簡單的單一值，例如一個整數 &lt;code&gt;importance&lt;/code&gt;。
如果直接寫 &lt;code&gt;importance: int&lt;/code&gt;，FastAPI 會把它當作&lt;strong&gt;查詢參數&lt;/strong&gt;，因為它是基本型別。&lt;/p&gt;
&lt;p&gt;如果堅持要把這個簡單的值也放進 JSON Body 裡面，可以使用 FastAPI 提供的 &lt;code&gt;Body()&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Body, FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.put&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_item&lt;/span&gt;(item_id: int, item: Item, importance: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Body(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;)):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style="color:#e6db74"&gt;&amp;#34;importance&amp;#34;&lt;/span&gt;: importance}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Body(...)&lt;/code&gt; 中的 &lt;code&gt;...&lt;/code&gt; 在 FastAPI 裡代表&lt;strong&gt;必填&lt;/strong&gt;的意思。&lt;/li&gt;
&lt;li&gt;此時，FastAPI 預期的 JSON 結構會變成：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;機械鍵盤&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3000.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;importance&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="3-使用-field-為欄位加上驗證"&gt;3. 使用 Field() 為欄位加上驗證
&lt;/h3&gt;&lt;p&gt;如果想要對 Pydantic 模型裡面的某個欄位做更嚴格的限制（例如：價格必須大於 0、名字長度最多 50 字元），需要使用 Pydantic 的 &lt;code&gt;Field()&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, Field
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, max_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;, description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;商品的名稱&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: str &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;商品詳細描述&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, gt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;價格必須大於 0&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的參數解釋如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;：代表必填。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_length=50&lt;/code&gt;：字串長度不能超過 50。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default=None&lt;/code&gt;：代表預設值為 &lt;code&gt;None&lt;/code&gt;（可選填）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gt=0&lt;/code&gt;：Greater Than，數值必須大於 0。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;description&lt;/code&gt; / &lt;code&gt;title&lt;/code&gt;：這兩個參數不會影響驗證，但會顯示在 Swagger UI 文件上，幫助前端工程師理解欄位意義。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="4-list-與-set處理陣列資料"&gt;4. List 與 Set：處理陣列資料
&lt;/h3&gt;&lt;p&gt;如果某個欄位會包含多個值（例如：商品的標籤清單），在 Python 中可以使用 &lt;code&gt;list&lt;/code&gt; 或 &lt;code&gt;set&lt;/code&gt; 型別。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tags: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; unique_tags: set[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list[str]&lt;/code&gt; 代表這個欄位必須是一個列表，且裡面的元素都要是字串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set[str]&lt;/code&gt; 與 list 類似，但如果是 &lt;code&gt;set&lt;/code&gt;，當前端傳來包含重複值的 JSON（例如 &lt;code&gt;[&amp;quot;電子&amp;quot;, &amp;quot;電子&amp;quot;, &amp;quot;促銷&amp;quot;]&lt;/code&gt;）時，FastAPI 轉成 Python &lt;code&gt;set&lt;/code&gt; 後會自動去重複，變成 &lt;code&gt;{&amp;quot;電子&amp;quot;, &amp;quot;促銷&amp;quot;}&lt;/code&gt;！&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="5-巢狀模型模型中的模型"&gt;5. 巢狀模型：模型中的模型
&lt;/h3&gt;&lt;p&gt;Pydantic 最強大的地方在於它的高階組合能力，可以把一個模型當作另一個模型中的欄位型別，創造出「巢狀結構」。&lt;/p&gt;
&lt;p&gt;例如，一個商品可能包含多個「圖片」：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, HttpUrl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Image&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; url: HttpUrl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 這個欄位是一個列表，且裡面的元素必須符合 Image 類&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; images: list[Image] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HttpUrl&lt;/code&gt; 是 Pydantic 內建的特殊型別，它會自動驗證傳入的字串是否為合法的網址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此時，FastAPI 預期的 JSON 就會是一個深層的巢狀結構：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;遊戲主機&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;15000.0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;images&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;https://example.com/console_front.png&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;正面照&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;https://example.com/console_back.png&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;背面照&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：建立包含清單的模型&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：建立一個 &lt;code&gt;Post&lt;/code&gt;（部落格文章）模型，包含以下欄位：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt;：字串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;content&lt;/code&gt;：字串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;comments&lt;/code&gt;：字串的列表（預設為空列表）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Post&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; comments: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：使用 Field() 加上限制&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：請修改上一題的 &lt;code&gt;Post&lt;/code&gt; 模型，要求 &lt;code&gt;title&lt;/code&gt; 的長度不可超過 100 個字元，並加上描述 &lt;code&gt;&amp;quot;文章的標題&amp;quot;&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, Field
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Post&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, max_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;文章的標題&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; comments: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body-multiple-params/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - Body 多個參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body-fields/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 欄位&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body-nested-models/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 巢狀模型&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI 參數驗證：Query() 與 Path() 的完整用法</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-parameter-validation/</link><pubDate>Tue, 28 Apr 2026 10:46:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-parameter-validation/</guid><description>&lt;p&gt;在第 2 篇和第 3 篇中，我們學會了宣告&lt;code&gt;路徑參數&lt;/code&gt;和&lt;code&gt;查詢參數&lt;/code&gt;，FastAPI 已經幫我們做好了最基礎的「型別轉換」與「必填/選填」檢查。
但如果需要更嚴格的限制呢？例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;密碼長度至少要 8 個字元。&lt;/li&gt;
&lt;li&gt;商品 ID 必須大於 0。&lt;/li&gt;
&lt;li&gt;查詢參數的名稱在網址上是 &lt;code&gt;item-query&lt;/code&gt;（帶有橫線，在 Python 中是不合法的變數名稱）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這時候，就需要 FastAPI 內建的 &lt;code&gt;Query()&lt;/code&gt; 與 &lt;code&gt;Path()&lt;/code&gt; 兩大工具了。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%ad%98%e6%ad%a3%e8%a6%8f%e8%a1%a8%e9%81%94%e5%bc%8f-regex" &gt;前置知識：正規表達式 (Regex)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e7%82%ba%e4%bb%80%e9%ba%bc%e9%9c%80%e8%a6%81-query-%e5%92%8c-path" &gt;為什麼需要 Query() 和 Path()？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-query-%e7%9a%84%e5%ad%97%e4%b8%b2%e8%88%87%e9%95%b7%e5%ba%a6%e9%a9%97%e8%ad%89" &gt;Query() 的字串與長度驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-query-%e8%a7%a3%e6%b1%ba%e5%8f%83%e6%95%b8%e5%88%a5%e5%90%8d%e8%88%87%e6%96%87%e4%bb%b6%e8%aa%aa%e6%98%8e" &gt;Query() 解決參數別名與文件說明&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e4%bd%bf%e7%94%a8-query-%e6%8e%a5%e6%94%b6%e5%a4%9a%e5%80%8b%e7%9b%b8%e5%90%8c%e5%90%8d%e7%a8%b1%e7%9a%84%e5%80%bc" &gt;使用 Query() 接收多個相同名稱的值&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-path-%e7%9a%84%e6%95%b8%e5%80%bc%e7%af%84%e5%9c%8d%e9%a9%97%e8%ad%89" &gt;Path() 的數值範圍驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e5%b0%87%e5%a4%9a%e5%80%8b%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8%e6%95%b4%e5%90%88%e6%88%90%e6%a8%a1%e5%9e%8b" &gt;將多個查詢參數整合成模型&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-前置知識正規表達式-regex"&gt;1. 前置知識：正規表達式 (Regex)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;正規表達式（Regular Expression，簡稱 Regex 或 RegExp）&lt;/strong&gt; 是一種用來「匹配字串模式」的語法。&lt;br&gt;
例如，如果要檢查一個字串是否完全由大寫和小寫的英文字母組成，可以使用正規表達式 &lt;code&gt;^[a-zA-Z]+$&lt;/code&gt;。&lt;br&gt;
FastAPI 允許我們直接把這段模式丟給它，它會自動幫我們檢查前端傳來的字串是否符合規則。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-為什麼需要-query-和-path"&gt;2. 為什麼需要 Query() 和 Path()？
&lt;/h3&gt;&lt;p&gt;之前在處理 Request Body 時，我們學過可以用 Pydantic 的 &lt;code&gt;Field()&lt;/code&gt; 來加上驗證，但&lt;strong&gt;查詢參數和路徑參數並不是 Pydantic 模型&lt;/strong&gt;，它們只是函式的參數。&lt;br&gt;
因此，FastAPI 提供了 &lt;code&gt;Query()&lt;/code&gt; 和 &lt;code&gt;Path()&lt;/code&gt;，讓你能為這些網址上的參數做到和 &lt;code&gt;Field()&lt;/code&gt; 一模一樣的驗證與文件宣告效果！&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="3-query-的字串與長度驗證"&gt;3. Query() 的字串與長度驗證
&lt;/h3&gt;&lt;p&gt;假設有一個搜尋 API，希望使用者傳入的關鍵字 &lt;code&gt;q&lt;/code&gt; 長度介於 3 到 50 個字元之間，並且只能包含英文字母：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI, Query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_items&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; q: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, min_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, max_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;, pattern&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;^[a-zA-Z]+$&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;results&amp;#34;&lt;/span&gt;: [{&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Foo&amp;#34;&lt;/span&gt;}, {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bar&amp;#34;&lt;/span&gt;}], &lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Query(None, ...)&lt;/code&gt; 的第一個參數 &lt;code&gt;None&lt;/code&gt; 代表這是選填的預設值，如果這是一個必填參數，可以寫成 &lt;code&gt;Query(...)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pattern&lt;/code&gt; 會在幕後使用 Python 的 &lt;code&gt;re&lt;/code&gt; 模組進行正規表達式比對，如果使用者傳了包含數字的 &lt;code&gt;?q=hello123&lt;/code&gt;，FastAPI 會直接回傳 422 錯誤。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="4-query-解決參數別名與文件說明"&gt;4. Query() 解決參數別名與文件說明
&lt;/h3&gt;&lt;p&gt;有些時候，前端習慣用帶有橫線的參數名稱（如 &lt;code&gt;item-query&lt;/code&gt;），但在 Python 中，變數名稱不能有橫線（&lt;code&gt;-&lt;/code&gt; 會被當作減號）。
這時可以使用 &lt;code&gt;alias&lt;/code&gt;（別名）來解決這個衝突。&lt;/p&gt;
&lt;p&gt;此外，也可以加上 &lt;code&gt;title&lt;/code&gt;、&lt;code&gt;description&lt;/code&gt; 甚至標記為 &lt;code&gt;deprecated&lt;/code&gt;（已棄用）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_items&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; q: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alias&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;item-query&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;搜尋關鍵字&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;輸入想尋找的商品名稱，必須是英文字母。&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; deprecated&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;results&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;Foo&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bar&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;在瀏覽器中，網址必須輸入 &lt;code&gt;http://127.0.0.1:8000/search/?item-query=foo&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在 Python 程式碼中，我們依然使用合法的變數名稱 &lt;code&gt;q&lt;/code&gt; 來接收它。&lt;/li&gt;
&lt;li&gt;在 Swagger UI 中，這個欄位會顯示詳細的中文說明，並且被畫上一條刪除線，提示開發者這個參數未來可能會被移除（&lt;code&gt;deprecated=True&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="5-使用-query-接收多個相同名稱的值"&gt;5. 使用 Query() 接收多個相同名稱的值
&lt;/h3&gt;&lt;p&gt;如果前端想要一次搜尋多個關鍵字，網址可能會長這樣：
&lt;code&gt;http://127.0.0.1:8000/items/?q=foo&amp;amp;q=bar&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;為了讓 FastAPI 把這兩個 &lt;code&gt;q&lt;/code&gt; 收集成一個 Python 列表（List），必須明確使用 &lt;code&gt;Query()&lt;/code&gt; 來宣告：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/multi&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_items_multi&lt;/span&gt;(q: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;apple&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;banana&amp;#34;&lt;/span&gt;])):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;q_list&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果不加上 &lt;code&gt;Query()&lt;/code&gt;（例如唯獨宣告 &lt;code&gt;q: str = None&lt;/code&gt;），當網址包含多個相同參數時，FastAPI 只會抓到&lt;strong&gt;最後一個值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;錯誤測試範例（只宣告 q: str）&lt;/strong&gt;：
發送請求：&lt;code&gt;GET http://127.0.0.1:8000/items/multi?q=foo&amp;amp;q=bar&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：如果在參數寫了 &lt;code&gt;q: list[str]&lt;/code&gt; 但卻&lt;strong&gt;沒有&lt;/strong&gt;加上 &lt;code&gt;= Query()&lt;/code&gt;，FastAPI 會把 &lt;code&gt;q&lt;/code&gt; 當作是要從 JSON Request Body 接收的資料，並拋出錯誤。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;加上了 &lt;code&gt;Query()&lt;/code&gt; 後，回傳的才會是正確且完整的陣列：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正確測試範例（宣告 q: list[str] = Query(&amp;hellip;)）&lt;/strong&gt;：
發送請求：&lt;code&gt;GET http://127.0.0.1:8000/items/multi?q=foo&amp;amp;q=bar&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;q_list&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="6-path-的數值範圍驗證"&gt;6. Path() 的數值範圍驗證
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Path()&lt;/code&gt; 的用法與 &lt;code&gt;Query()&lt;/code&gt; 幾乎完全一樣，唯一的差別是：&lt;strong&gt;它專門用在路徑參數上&lt;/strong&gt;。
由於路徑參數通常是 ID（數字），最常使用它來做數值範圍的限制。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_user&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user_id: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Path(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;使用者 ID&amp;#34;&lt;/span&gt;, ge&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, le&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;數值驗證參數非常直觀：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ge&lt;/code&gt;：Greater than or Equal（大於等於 &lt;code&gt;≥&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gt&lt;/code&gt;：Greater Than（大於 &lt;code&gt;&amp;gt;&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;le&lt;/code&gt;：Less than or Equal（小於等於 &lt;code&gt;≤&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lt&lt;/code&gt;：Less Than（小於 &lt;code&gt;&amp;lt;&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在這個例子中，如果輸入 &lt;code&gt;/users/0&lt;/code&gt; 或 &lt;code&gt;/users/1001&lt;/code&gt;，都會被 FastAPI 的驗證機制阻擋下來。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;錯誤測試範例&lt;/strong&gt;：
發送請求：&lt;code&gt;GET http://127.0.0.1:8000/users/-14&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;greater_than_equal&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be greater than or equal to 1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;-14&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;ctx&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;ge&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="7-將多個查詢參數整合成模型"&gt;7. 將多個查詢參數整合成模型
&lt;/h3&gt;&lt;p&gt;如果一個 API 有高達十幾個查詢參數（例如搜尋頁面有分類、價格上下限、排序方式、頁碼&amp;hellip;），路由函式的參數列表會變得長得嚇人。&lt;/p&gt;
&lt;p&gt;在較新的 FastAPI 版本中，可以把這些「查詢參數」也打包成一個 Pydantic 模型，只要在依賴注入時加上 &lt;code&gt;Depends()&lt;/code&gt; 即可。（關於 &lt;code&gt;Depends()&lt;/code&gt; 我們會在後面的章節詳細介紹，現在先記住這個好用的寫法）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Depends
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, Field
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FilterParams&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; limit: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, gt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, le&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; offset: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, ge&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order_by: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/products/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_products&lt;/span&gt;(filters: FilterParams &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Depends()):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;filters&amp;#34;&lt;/span&gt;: filters}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;現在，只要客戶端造訪 &lt;code&gt;/products/?limit=50&amp;amp;offset=10&amp;amp;order_by=price&lt;/code&gt;，FastAPI 就會自動幫你把這些散落的查詢參數，組裝成一個漂亮的 &lt;code&gt;FilterParams&lt;/code&gt; 物件。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：混合驗證&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 &lt;code&gt;GET /books/{book_id}&lt;/code&gt; 的端點。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路徑參數 &lt;code&gt;book_id&lt;/code&gt; 必須是整數，且大於等於 1。&lt;/li&gt;
&lt;li&gt;查詢參數 &lt;code&gt;author&lt;/code&gt; 是選填的字串，長度至少 2 個字元。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI, Path, Query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/books/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{book_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_book&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; book_id: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Path(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, ge&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, min_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;book_id&amp;#34;&lt;/span&gt;: book_id, &lt;span style="color:#e6db74"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;: author}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：陣列查詢參數&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 &lt;code&gt;GET /tags/&lt;/code&gt; 端點。
它可以接收多個同名的查詢參數 &lt;code&gt;tag&lt;/code&gt;（例如：&lt;code&gt;?tag=python&amp;amp;tag=web&lt;/code&gt;）。請確保 &lt;code&gt;tag&lt;/code&gt; 是必填的陣列。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI, Query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/tags/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_tags&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tag: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: tag}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的 &lt;code&gt;Query(...)&lt;/code&gt; 中的 &lt;code&gt;...&lt;/code&gt; 代表這是必填的。如果不提供任何 &lt;code&gt;?tag=&lt;/code&gt;，將會拋出 422 錯誤。&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/query-params-str-validations/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 查詢參數與字串驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/path-params-numeric-validations/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 路徑參數與數值驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/query-param-models/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 查詢參數模型&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>