[{"data":1,"prerenderedAt":1121},["ShallowReactive",2],{"navigation_docs":3,"-web-dev-network-restful-api-principles":352,"-web-dev-network-restful-api-principles-surround":1116},[4,54,83,116,125,161,179,200],{"title":5,"path":6,"stem":7,"children":8},"Ai","/ai","ai",[9,12,42,46,50],{"title":10,"path":6,"stem":11},"AI 技術探索","ai/index",{"title":13,"path":14,"stem":15,"children":16,"page":41},"Rag","/ai/rag","ai/RAG",[17,21,25,29,33,37],{"title":18,"path":19,"stem":20},"BM25 演算法深入解析：從 TF-IDF 到現代搜尋引擎的核心技術","/ai/rag/bm25-deep-dive-from-tf-idf-to-rag","ai/RAG/bm25-deep-dive-from-tf-idf-to-rag",{"title":22,"path":23,"stem":24},"Dense Retrieval（向量檢索）深入解析","/ai/rag/dense-retrieval-deep-dive","ai/RAG/dense-retrieval-deep-dive",{"title":26,"path":27,"stem":28},"RAG（Retrieval-Augmented Generation）原理與實作入門","/ai/rag/rag-intro-principles-and-implementation","ai/RAG/rag-intro-principles-and-implementation",{"title":30,"path":31,"stem":32},"RAG Retrieval Strategy 深入解析","/ai/rag/rag-retrieval-strategy-deep-dive","ai/RAG/rag-retrieval-strategy-deep-dive",{"title":34,"path":35,"stem":36},"Reciprocal Rank Fusion（RRF）教學：打造更穩定的檢索融合策略","/ai/rag/rrf-reciprocal-rank-fusion-guide","ai/RAG/rrf-reciprocal-rank-fusion-guide",{"title":38,"path":39,"stem":40},"TF-IDF（Term Frequency–Inverse Document Frequency）深入解析","/ai/rag/tf-idf-deep-dive","ai/RAG/tf-idf-deep-dive",false,{"title":43,"path":44,"stem":45},"Docus AI 功能完整實作指南","/ai/docus-ai-implementation","ai/docus-ai-implementation",{"title":47,"path":48,"stem":49},"llms.txt 是什麼？在 Nuxt 中控制 AI 與搜尋引擎爬蟲的新方式","/ai/llms-txt","ai/llms-txt",{"title":51,"path":52,"stem":53},"MCP（Model Context Protocol）是什麼？用「AI 的 USB-C」理解模型如何安全連接工具與資料","/ai/mcp_concept","ai/mcp_concept",{"title":55,"path":56,"stem":57,"children":58,"page":41},"Data Analysis","/data-analysis","data-analysis",[59,63,67,71,75,79],{"title":60,"path":61,"stem":62},"Exploratory Data Analysis（EDA）資料探索分析入門","/data-analysis/exploratory-data-analysis-eda","data-analysis/exploratory-data-analysis-eda",{"title":64,"path":65,"stem":66},"PR-AUC 深入解析：不平衡分類問題的重要評估指標","/data-analysis/pr-auc-for-imbalanced-classification","data-analysis/pr-auc-for-imbalanced-classification",{"title":68,"path":69,"stem":70},"ROC-AUC 深入解析：理解分類模型的判斷能力","/data-analysis/roc-auc-complete-guide","data-analysis/roc-auc-complete-guide",{"title":72,"path":73,"stem":74},"SHAP（SHapley Additive exPlanations）模型解釋方法深入解析","/data-analysis/shap-model-interpretation-complete-guide","data-analysis/shap-model-interpretation-complete-guide",{"title":76,"path":77,"stem":78},"Social Network Analysis（SNA）深入解析","/data-analysis/social-network-analysis-sna","data-analysis/social-network-analysis-sna",{"title":80,"path":81,"stem":82},"Time-based Split：為什麼時間序列資料不能使用 Random Split？","/data-analysis/time-based-split-vs-random-split","data-analysis/time-based-split-vs-random-split",{"title":84,"path":85,"stem":86,"children":87,"page":41},"MachineLearning","/machine_learning","machine_learning",[88,92,96,100,104,108,112],{"title":89,"path":90,"stem":91},"Confusion Matrix（混淆矩陣）完整解析","/machine_learning/confusion-matrix","machine_learning/confusion-matrix",{"title":93,"path":94,"stem":95},"Decision Tree 模型完整教學：從原理到 Python 實作","/machine_learning/decision-tree-complete-guide","machine_learning/decision-tree-complete-guide",{"title":97,"path":98,"stem":99},"為什麼 Decision Tree 在 Tabular Data 上常常勝過 Deep Learning？","/machine_learning/decision-tree-vs-deep-learning-on-tabular-data","machine_learning/decision-tree-vs-deep-learning-on-tabular-data",{"title":101,"path":102,"stem":103},"LightGBM 模型入門：理解高效能的 Gradient Boosting 演算法","/machine_learning/lightgbm-intro-for-tabular-data","machine_learning/lightgbm-intro-for-tabular-data",{"title":105,"path":106,"stem":107},"Logistic Regression（Logit 回歸）完整教學","/machine_learning/logistic-regression-complete-guide","machine_learning/logistic-regression-complete-guide",{"title":109,"path":110,"stem":111},"為什麼模型訓練需要 Train / Validation / Test Dataset？","/machine_learning/train-validation-test-dataset","machine_learning/train-validation-test-dataset",{"title":113,"path":114,"stem":115},"XGBoost 原理與實務教學","/machine_learning/xgboost-complete-guide","machine_learning/xgboost-complete-guide",{"title":117,"path":118,"stem":119,"children":120,"page":41},"Network","/network","network",[121],{"title":122,"path":123,"stem":124},"DNS 設定入門：CNAME vs A Record 完整解析","/network/dns-cname-vs-a-record-guide","network/dns-cname-vs-a-record-guide",{"title":126,"path":127,"stem":128,"children":129},"Nuxt","/nuxt","nuxt",[130,133,137,141,145,149,153,157],{"title":131,"path":127,"stem":132},"Nuxt 實戰指南","nuxt/index",{"title":134,"path":135,"stem":136},"Abstraction Layer 與 Adapter 的差別，一次搞懂設計角色","/nuxt/adapter_abstraction_layer","nuxt/adapter_abstraction_layer",{"title":138,"path":139,"stem":140},"Nuxt 4 全面理解 — 專案檔案結構解析","/nuxt/directory_structure","nuxt/directory_structure",{"title":142,"path":143,"stem":144},"Nuxt 前端 build 流程完整解析","/nuxt/frontend_build","nuxt/frontend_build",{"title":146,"path":147,"stem":148},"Nitro 是什麼？從 Nuxt 專案到 Server 與 Edge 的關鍵引擎","/nuxt/nitro","nuxt/nitro",{"title":150,"path":151,"stem":152},"深入理解 Nuxt 的 .nuxt 與 .output：為什麼部署時一定要分清楚？","/nuxt/nuxt_and_output","nuxt/nuxt_and_output",{"title":154,"path":155,"stem":156},"Nuxt 中 dev、build、preview 的差異一次搞懂","/nuxt/nuxt_dev_build_preview","nuxt/nuxt_dev_build_preview",{"title":158,"path":159,"stem":160},"Nuxt Image vs \u003Cimg>：為什麼 Nuxt 3 專案幾乎都該用 \u003CNuxtImg>？","/nuxt/nuxt_image","nuxt/nuxt_image",{"title":162,"path":163,"stem":164,"children":165},"Python 程式開發","/python","python/index",[166,167,171,175],{"title":162,"path":163,"stem":164},{"title":168,"path":169,"stem":170},"如何在 FastAPI 中調用記憶體 Buffer 回傳圖片 (附實例)","/python/memory_buffer","python/memory_buffer",{"title":172,"path":173,"stem":174},"Python 單元測試教學：如何為你的程式撰寫 Test","/python/python-unit-testing-unittest-pytest","python/python-unit-testing-unittest-pytest",{"title":176,"path":177,"stem":178},"如何使用 uv 取代 pip：改善 Python 專案的開發流程","/python/uv","python/uv",{"title":180,"path":181,"stem":182,"children":183,"page":41},"Statistic","/statistic","statistic",[184,188,192,196],{"title":185,"path":186,"stem":187},"卡方檢定（Chi-Square Test）入門教學","/statistic/chi-square-test-intro","statistic/chi-square-test-intro",{"title":189,"path":190,"stem":191},"常見抽樣方法（Sampling Methods）教學：Stratified、Quota、Convenience、Systematic、Simple Random","/statistic/common-sampling-methods-guide","statistic/common-sampling-methods-guide",{"title":193,"path":194,"stem":195},"Cramér’s V 指標介紹：如何衡量兩個類別變數之間的關聯","/statistic/cramers-v-for-categorical-association","statistic/cramers-v-for-categorical-association",{"title":197,"path":198,"stem":199},"Mann–Whitney U 與 Kolmogorov–Smirnov（KS）檢定：非參數統計檢定的入門指南","/statistic/mann-whitney-u-and-ks-test-intro","statistic/mann-whitney-u-and-ks-test-intro",{"title":201,"path":202,"stem":203,"children":204},"WebDev","/web_dev","web_dev",[205,208,234,248,282,312,334],{"title":206,"path":202,"stem":207},"Web 前端開發","web_dev/index",{"title":209,"path":210,"stem":211,"children":212},"瀏覽器與渲染","/web_dev/browser","web_dev/browser/index",[213,214,218,222,226,230],{"title":209,"path":210,"stem":211},{"title":215,"path":216,"stem":217},"瀏覽器儲存空間完整解析：Cookie、localStorage、IndexedDB 到 Cache Storage","/web_dev/browser/browser-storage-comprehensive-guide","web_dev/browser/browser-storage-comprehensive-guide",{"title":219,"path":220,"stem":221},"DOM (Document Object Model) 深入解析","/web_dev/browser/dom","web_dev/browser/dom",{"title":223,"path":224,"stem":225},"Service Worker Request Flow 深入解析","/web_dev/browser/service-worker-request-flow","web_dev/browser/service-worker-request-flow",{"title":227,"path":228,"stem":229},"CSR、SSR 與 SSG 是什麼？前端渲染策略完整比較","/web_dev/browser/ssr_csr_ssg","web_dev/browser/ssr_csr_ssg",{"title":231,"path":232,"stem":233},"Virtual DOM 深入解析：為什麼它能優化前端效能？","/web_dev/browser/virtual_dom","web_dev/browser/virtual_dom",{"title":235,"path":236,"stem":237,"children":238},"圖形技術","/web_dev/graphics","web_dev/graphics/index",[239,240,244],{"title":235,"path":236,"stem":237},{"title":241,"path":242,"stem":243},"為什麼 Three.js 專案幾乎都選擇 CSR？從 SSR 問題談起","/web_dev/graphics/threejs_csr","web_dev/graphics/threejs_csr",{"title":245,"path":246,"stem":247},"WebGL 是什麼？為什麼前端 3D 幾乎都靠它？","/web_dev/graphics/webgl","web_dev/graphics/webGL",{"title":249,"path":250,"stem":251,"children":252},"架構與配置","/web_dev/infrastructure","web_dev/infrastructure/index",[253,254,258,262,266,270,274,278],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257},"Zeabur + K3s + Nuxt 部署架構完整解析","/web_dev/infrastructure/k3s-zeabur","web_dev/infrastructure/K3s-zeabur",{"title":259,"path":260,"stem":261},"ECS vs Docker vs Kubernetes：從部署堆疊理解三層架構","/web_dev/infrastructure/ecs-docker-kubernetes-stack","web_dev/infrastructure/ecs-docker-kubernetes-stack",{"title":263,"path":264,"stem":265},"ECS vs VPS 是什麼？從部署網站的角度一次搞懂差別","/web_dev/infrastructure/ecs-vs-vps","web_dev/infrastructure/ecs-vs-vps",{"title":267,"path":268,"stem":269},"Zeabur 內網服務連線完整入門指南","/web_dev/infrastructure/k3s-internal-networking","web_dev/infrastructure/k3s-internal-networking",{"title":271,"path":272,"stem":273},"使用 Microservices 架構設計系統的優勢解析","/web_dev/infrastructure/microservices-architecture-advantages","web_dev/infrastructure/microservices-architecture-advantages",{"title":275,"path":276,"stem":277},"Nginx 入門教學：從反向代理到與 Kubernetes 的架構比較","/web_dev/infrastructure/nginx-intro","web_dev/infrastructure/nginx-intro",{"title":279,"path":280,"stem":281},"YAML 配置是什麼？為什麼現代開發都在用它","/web_dev/infrastructure/yaml-configuration","web_dev/infrastructure/yaml-configuration",{"title":283,"path":284,"stem":285,"children":286},"網絡與通訊","/web_dev/network","web_dev/network/index",[287,288,292,296,300,304,308],{"title":283,"path":284,"stem":285},{"title":289,"path":290,"stem":291},"HTTP Request 結構完整解析：從 Request Line 到 Header 一次看懂","/web_dev/network/http-request-structure","web_dev/network/http-request-structure",{"title":293,"path":294,"stem":295},"OSI 模型（Open Systems Interconnection Model）完整解析","/web_dev/network/osi-model","web_dev/network/osi-model",{"title":297,"path":298,"stem":299},"RESTful API 設計原則與實務解析","/web_dev/network/restful-api","web_dev/network/restful-api",{"title":301,"path":302,"stem":303},"RESTful API 五大設計原則深入解析","/web_dev/network/restful-api-principles","web_dev/network/restful-api-principles",{"title":305,"path":306,"stem":307},"Server-Sent Events (SSE) 深入解析與實作教學","/web_dev/network/sse-introduction","web_dev/network/sse-introduction",{"title":309,"path":310,"stem":311},"WebSocket 入門教學：從概念到 Node.js 實作","/web_dev/network/websocket-introduction","web_dev/network/websocket-introduction",{"title":313,"path":314,"stem":315,"children":316},"認證與安全","/web_dev/security","web_dev/security/index",[317,318,322,326,330],{"title":313,"path":314,"stem":315},{"title":319,"path":320,"stem":321},"使用 Cookie 與 Session 建立使用者驗證（完整新手教學）","/web_dev/security/cookie-session-authentication","web_dev/security/cookie-session-authentication",{"title":323,"path":324,"stem":325},"JWT 驗證機制完整解析：從登入流程到實務應用","/web_dev/security/jwt-authentication","web_dev/security/jwt-authentication",{"title":327,"path":328,"stem":329},"Secret Key 簽名是什麼？從零理解資料簽名的本質","/web_dev/security/secret-key-signing","web_dev/security/secret-key-signing",{"title":331,"path":332,"stem":333},"SSH（Secure Shell）是什麼？從遠端登入到安全通道的核心概念","/web_dev/security/ssh","web_dev/security/ssh",{"title":335,"path":336,"stem":337,"children":338},"SEO 與規範","/web_dev/seo","web_dev/seo/index",[339,340,344,348],{"title":335,"path":336,"stem":337},{"title":341,"path":342,"stem":343},"Canonical URL 是什麼？用生活化方式搞懂前端 SEO 的基本保命符","/web_dev/seo/canonical_link","web_dev/seo/canonical_link",{"title":345,"path":346,"stem":347},"robots.txt 是什麼？SEO 的第一道守門員","/web_dev/seo/robot_txt","web_dev/seo/robot_txt",{"title":349,"path":350,"stem":351},"Nuxt SEO 中的 Meta 與 SEO 工具是如何運作的？","/web_dev/seo/seo","web_dev/seo/seo",{"id":353,"title":301,"body":354,"description":1102,"extension":1103,"links":1104,"meta":1105,"navigation":1113,"path":302,"seo":1114,"stem":303,"__hash__":1115},"docs/web_dev/network/restful-api-principles.md",{"type":355,"value":356,"toc":1070},"minimark",[357,361,370,373,381,384,387,392,397,404,407,420,423,427,438,441,464,467,470,481,483,487,490,493,500,503,506,509,543,546,557,560,571,577,579,583,586,589,600,603,606,623,626,642,645,656,659,676,679,693,695,699,702,706,709,724,727,787,791,794,807,810,814,817,828,831,844,847,851,854,938,944,946,950,953,956,970,973,998,1001,1004,1012,1014,1018,1021,1039,1041,1044,1051,1053,1056,1066],[358,359,301],"h1",{"id":360},"restful-api-五大設計原則深入解析",[362,363,364,365,369],"p",{},"在討論 RESTful API 時，我們常聽到「五項設計原則」或「REST constraints」。這些原則並不是隨意訂出來的規則，而是由 ",[366,367,368],"strong",{},"Roy Fielding"," 在其博士論文中提出，用來描述一種適合 Web 架構的系統設計風格。",[362,371,372],{},"理解這五個原則的關鍵，不是背起來，而是知道：",[374,375,376],"blockquote",{},[362,377,378],{},[366,379,380],{},"每一個 constraint 都在解決「可擴充性、可維護性、效能或一致性」的問題。",[362,382,383],{},"以下我會用「解決什麼問題 → 怎麼做 → 實務例子與常見坑」的方式，帶你真正看懂 RESTful 的設計思維。",[385,386],"hr",{},[388,389,391],"h2",{"id":390},"_1️⃣-clientserver前後端分離","1️⃣ Client–Server（前後端分離）",[393,394,396],"h3",{"id":395},"它在解決什麼問題","它在解決什麼問題？",[362,398,399,400,403],{},"Web 系統會不斷演進。如果畫面與資料處理混在一起，每次改動都會牽一髮動全身。Client–Server constraint 的核心目的，是 ",[366,401,402],{},"讓使用者介面與資料邏輯解耦","。",[362,405,406],{},"這樣的好處是：",[408,409,410,414,417],"ul",{},[411,412,413],"li",{},"前端可以改框架（Vue → React）",[411,415,416],{},"後端可以換語言（Node → Go）",[411,418,419],{},"API 只要合約不變，雙方都能獨立演進",[362,421,422],{},"這正是現代 SPA + API 架構（例如 Nuxt + REST API）的基礎。",[393,424,426],{"id":425},"怎麼做","怎麼做？",[408,428,429,432,435],{},[411,430,431],{},"Client 只負責 UI 與發送 HTTP request",[411,433,434],{},"Server 只負責資料存取與業務規則",[411,436,437],{},"雙方透過清楚的 API schema 溝通（JSON 格式）",[362,439,440],{},"例如：",[442,443,448],"pre",{"className":444,"code":445,"language":446,"meta":447,"style":447},"language-bash shiki shiki-themes github-dark","GET /api/posts\n","bash","",[449,450,451],"code",{"__ignoreMap":447},[452,453,456,460],"span",{"class":454,"line":455},"line",1,[452,457,459],{"class":458},"svObZ","GET",[452,461,463],{"class":462},"sU2Wk"," /api/posts\n",[362,465,466],{},"前端只知道這個 endpoint 會回傳 JSON，不需要知道背後是單體架構還是微服務。",[393,468,469],{"id":469},"常見坑",[408,471,472,475,478],{},[411,473,474],{},"Server 回傳 HTML 片段給 SPA（畫面邏輯回流）",[411,476,477],{},"API 沒有版本管理，導致 client 被迫同步修改",[411,479,480],{},"request/response 格式頻繁變動，沒有穩定合約",[385,482],{},[388,484,486],{"id":485},"_2️⃣-stateless無狀態","2️⃣ Stateless（無狀態）",[393,488,396],{"id":489},"它在解決什麼問題-1",[362,491,492],{},"Stateless 的核心目標是：",[374,494,495],{},[362,496,497],{},[366,498,499],{},"讓每個 request 都可以被「任何一台伺服器」處理。",[362,501,502],{},"如果 server 把使用者狀態存在記憶體裡，當你擴充成多台機器時，就會出現 sticky session 問題，負載平衡變得困難。",[393,504,426],{"id":505},"怎麼做-1",[362,507,508],{},"每個 request 都要帶齊資訊，例如：",[442,510,514],{"className":511,"code":512,"language":513,"meta":447,"style":447},"language-sql shiki shiki-themes github-dark","GET /orders\nAuthorization: Bearer \u003Ctoken>\n","sql",[449,515,516,528],{"__ignoreMap":447},[452,517,518,521,524],{"class":454,"line":455},[452,519,459],{"class":520},"snl16",[452,522,523],{"class":520}," /",[452,525,527],{"class":526},"s95oV","orders\n",[452,529,531,534,537,540],{"class":454,"line":530},2,[452,532,533],{"class":526},"Authorization: Bearer ",[452,535,536],{"class":520},"\u003C",[452,538,539],{"class":526},"token",[452,541,542],{"class":520},">\n",[362,544,545],{},"Server 不需要記住你是誰，每次都從 token 驗證身份。\n狀態如果需要保存，應該：",[408,547,548,551,554],{},[411,549,550],{},"放在資料庫",[411,552,553],{},"放在 Redis",[411,555,556],{},"或由 client 持有（例如 JWT）",[393,558,469],{"id":559},"常見坑-1",[408,561,562,565,568],{},[411,563,564],{},"使用 server memory 存 session",[411,566,567],{},"設計流程式 API（必須先呼叫 A 才能呼叫 B，中間靠 server 記狀態）",[411,569,570],{},"認為 Stateless 等於「系統不能有狀態」",[362,572,573,574,403],{},"特別釐清：Stateless 不代表沒有狀態，而是 ",[366,575,576],{},"不要把狀態綁在單一伺服器記憶體上",[385,578],{},[388,580,582],{"id":581},"_3️⃣-cacheable可快取","3️⃣ Cacheable（可快取）",[393,584,396],{"id":585},"它在解決什麼問題-2",[362,587,588],{},"Web 的流量與效能瓶頸，很多來自於重複請求。Cacheable constraint 的目的是：",[408,590,591,594,597],{},[411,592,593],{},"減少伺服器負擔",[411,595,596],{},"降低延遲",[411,598,599],{},"降低成本\n讓 CDN、proxy、browser 幫你分擔壓力。",[393,601,426],{"id":602},"怎麼做-2",[362,604,605],{},"對適合快取的回應設定：",[442,607,611],{"className":608,"code":609,"language":610,"meta":447,"style":447},"language-arduino shiki shiki-themes github-dark","Cache-Control: public, max-age=60\nETag: \"abc123\"\n","arduino",[449,612,613,618],{"__ignoreMap":447},[452,614,615],{"class":454,"line":455},[452,616,617],{},"Cache-Control: public, max-age=60\n",[452,619,620],{"class":454,"line":530},[452,621,622],{},"ETag: \"abc123\"\n",[362,624,625],{},"並支援條件式請求：",[442,627,629],{"className":511,"code":628,"language":513,"meta":447,"style":447},"If-None-Match: \"abc123\"\n",[449,630,631],{"__ignoreMap":447},[452,632,633,636,639],{"class":454,"line":455},[452,634,635],{"class":520},"If-None-Match",[452,637,638],{"class":526},": ",[452,640,641],{"class":462},"\"abc123\"\n",[362,643,644],{},"當資源未改變時，回傳：",[442,646,650],{"className":647,"code":648,"language":649,"meta":447,"style":447},"language-mathematica shiki shiki-themes github-dark","304 Not Modified\n","mathematica",[449,651,652],{"__ignoreMap":447},[452,653,654],{"class":454,"line":455},[452,655,648],{},[393,657,658],{"id":658},"實務例子",[408,660,661,667],{},[411,662,663,666],{},[449,664,665],{},"/posts"," 公開文章列表 → 可快取",[411,668,669,672,673],{},[449,670,671],{},"/me"," 使用者個資 → ",[449,674,675],{},"private, no-store",[393,677,469],{"id":678},"常見坑-2",[408,680,681,684,690],{},[411,682,683],{},"完全沒設定 cache header",[411,685,686,687],{},"將含個資的資料設為 ",[449,688,689],{},"public",[411,691,692],{},"POST/PUT 後沒有處理 cache 失效策略",[385,694],{},[388,696,698],{"id":697},"_4️⃣-uniform-interface統一介面rest-的核心","4️⃣ Uniform Interface（統一介面）——REST 的核心",[362,700,701],{},"在五個 constraint 中，Uniform Interface 是最核心的一個。它讓 API 具有一致性與可預測性。",[393,703,705],{"id":704},"a-resource-based以資源為中心","(a) Resource-based（以資源為中心）",[362,707,708],{},"URL 用名詞，而不是動詞：",[442,710,712],{"className":444,"code":711,"language":446,"meta":447,"style":447},"/users\n/posts/1\n",[449,713,714,719],{"__ignoreMap":447},[452,715,716],{"class":454,"line":455},[452,717,718],{"class":458},"/users\n",[452,720,721],{"class":454,"line":530},[452,722,723],{"class":458},"/posts/1\n",[362,725,726],{},"行為交給 HTTP method：",[728,729,730,744],"table",{},[731,732,733],"thead",{},[734,735,736,741],"tr",{},[737,738,740],"th",{"align":739},"left","Method",[737,742,743],{"align":739},"意義",[745,746,747,755,763,771,779],"tbody",{},[734,748,749,752],{},[750,751,459],"td",{"align":739},[750,753,754],{"align":739},"讀取",[734,756,757,760],{},[750,758,759],{"align":739},"POST",[750,761,762],{"align":739},"新增",[734,764,765,768],{},[750,766,767],{"align":739},"PUT",[750,769,770],{"align":739},"完整更新",[734,772,773,776],{},[750,774,775],{"align":739},"PATCH",[750,777,778],{"align":739},"部分更新",[734,780,781,784],{},[750,782,783],{"align":739},"DELETE",[750,785,786],{"align":739},"刪除",[393,788,790],{"id":789},"b-representation資源的表徵","(b) Representation（資源的表徵）",[362,792,793],{},"Client 操作的是資源的「表示」，通常是 JSON：",[442,795,797],{"className":444,"code":796,"language":446,"meta":447,"style":447},"Accept: application/json\n",[449,798,799],{"__ignoreMap":447},[452,800,801,804],{"class":454,"line":455},[452,802,803],{"class":458},"Accept:",[452,805,806],{"class":462}," application/json\n",[362,808,809],{},"同一資源可以有不同表示（例如 JSON、CSV）。",[393,811,813],{"id":812},"c-self-descriptive-messages自我描述訊息","(c) Self-descriptive messages（自我描述訊息）",[362,815,816],{},"Response 必須清楚：",[408,818,819,822,825],{},[411,820,821],{},"正確的 HTTP status code",[411,823,824],{},"明確的 Content-Type",[411,826,827],{},"一致的錯誤格式",[362,829,830],{},"例如驗證失敗應回：",[442,832,834],{"className":444,"code":833,"language":446,"meta":447,"style":447},"401 Unauthorized\n",[449,835,836],{"__ignoreMap":447},[452,837,838,841],{"class":454,"line":455},[452,839,840],{"class":458},"401",[452,842,843],{"class":462}," Unauthorized\n",[362,845,846],{},"而不是 200 + 錯誤字串。",[393,848,850],{"id":849},"d-hateoas超媒體作為應用狀態引擎","(d) HATEOAS（超媒體作為應用狀態引擎）",[362,852,853],{},"Response 可以包含相關連結：",[442,855,859],{"className":856,"code":857,"language":858,"meta":447,"style":447},"language-json shiki shiki-themes github-dark","{\n  \"id\": 1,\n  \"title\": \"Post\",\n  \"links\": {\n    \"self\": \"/posts/1\",\n    \"delete\": \"/posts/1\"\n  }\n}\n","json",[449,860,861,866,880,893,902,915,926,932],{"__ignoreMap":447},[452,862,863],{"class":454,"line":455},[452,864,865],{"class":526},"{\n",[452,867,868,872,874,877],{"class":454,"line":530},[452,869,871],{"class":870},"sDLfK","  \"id\"",[452,873,638],{"class":526},[452,875,876],{"class":870},"1",[452,878,879],{"class":526},",\n",[452,881,883,886,888,891],{"class":454,"line":882},3,[452,884,885],{"class":870},"  \"title\"",[452,887,638],{"class":526},[452,889,890],{"class":462},"\"Post\"",[452,892,879],{"class":526},[452,894,896,899],{"class":454,"line":895},4,[452,897,898],{"class":870},"  \"links\"",[452,900,901],{"class":526},": {\n",[452,903,905,908,910,913],{"class":454,"line":904},5,[452,906,907],{"class":870},"    \"self\"",[452,909,638],{"class":526},[452,911,912],{"class":462},"\"/posts/1\"",[452,914,879],{"class":526},[452,916,918,921,923],{"class":454,"line":917},6,[452,919,920],{"class":870},"    \"delete\"",[452,922,638],{"class":526},[452,924,925],{"class":462},"\"/posts/1\"\n",[452,927,929],{"class":454,"line":928},7,[452,930,931],{"class":526},"  }\n",[452,933,935],{"class":454,"line":934},8,[452,936,937],{"class":526},"}\n",[362,939,940,941,403],{},"讓 client 透過連結探索流程，而不是寫死路徑。雖然很多 API 沒完全實作，但核心概念是：",[366,942,943],{},"API 應該引導 client，而不是讓 client 硬編碼流程",[385,945],{},[388,947,949],{"id":948},"_5️⃣-layered-system分層系統","5️⃣ Layered System（分層系統）",[393,951,396],{"id":952},"它在解決什麼問題-3",[362,954,955],{},"讓系統可以在 client 不知情的情況下插入中介層，提升安全性、可擴充性與可維護性。例如：",[408,957,958,961,964,967],{},[411,959,960],{},"CDN",[411,962,963],{},"API Gateway",[411,965,966],{},"Load Balancer",[411,968,969],{},"WAF",[393,971,972],{"id":972},"架構例子",[442,974,978],{"className":975,"code":976,"language":977,"meta":447,"style":447},"language-css shiki shiki-themes github-dark","Client ↓ CDN ↓ API Gateway ↓ Service A / Service B ↓ Database\n","css",[449,979,980],{"__ignoreMap":447},[452,981,982,985,989,992,995],{"class":454,"line":455},[452,983,984],{"class":526},"Client ↓ CDN ↓ API Gateway ↓ Service ",[452,986,988],{"class":987},"s4JwU","A",[452,990,991],{"class":526}," / Service ",[452,993,994],{"class":987},"B",[452,996,997],{"class":526}," ↓ Database\n",[362,999,1000],{},"Client 只知道一個入口。",[393,1002,469],{"id":1003},"常見坑-3",[408,1005,1006,1009],{},[411,1007,1008],{},"Client 直接依賴內部服務位址",[411,1010,1011],{},"Response 缺少必要 header，導致中介層無法正確快取",[385,1013],{},[388,1015,1017],{"id":1016},"一個實用的-rest-檢查清單","一個實用的 REST 檢查清單",[362,1019,1020],{},"設計 API 時，可以快速問自己：",[1022,1023,1024,1027,1030,1033,1036],"ol",{},[411,1025,1026],{},"URL 是名詞資源嗎？",[411,1028,1029],{},"GET 是否無副作用？",[411,1031,1032],{},"每個 request 是否獨立完整？",[411,1034,1035],{},"Status code 是否正確？",[411,1037,1038],{},"是否能在前面加 CDN 而不改 client？",[385,1040],{},[388,1042,1043],{"id":1043},"小結",[362,1045,1046,1047,1050],{},"REST 並不是「用 JSON + HTTP method」這麼簡單，而是一套為了 ",[366,1048,1049],{},"可擴充性與長期演進"," 設計的架構風格。五個 constraint 彼此配合，讓系統能「活得更久」。",[385,1052],{},[388,1054,1055],{"id":1055},"參考資料",[408,1057,1058],{},[411,1059,1060,1061,1065],{},"Roy Fielding, ",[1062,1063,1064],"em",{},"Architectural Styles and the Design of Network-based Software Architectures",", Doctoral Dissertation, 2000.",[1067,1068,1069],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}",{"title":447,"searchDepth":530,"depth":530,"links":1071},[1072,1077,1082,1088,1094,1099,1100,1101],{"id":390,"depth":530,"text":391,"children":1073},[1074,1075,1076],{"id":395,"depth":882,"text":396},{"id":425,"depth":882,"text":426},{"id":469,"depth":882,"text":469},{"id":485,"depth":530,"text":486,"children":1078},[1079,1080,1081],{"id":489,"depth":882,"text":396},{"id":505,"depth":882,"text":426},{"id":559,"depth":882,"text":469},{"id":581,"depth":530,"text":582,"children":1083},[1084,1085,1086,1087],{"id":585,"depth":882,"text":396},{"id":602,"depth":882,"text":426},{"id":658,"depth":882,"text":658},{"id":678,"depth":882,"text":469},{"id":697,"depth":530,"text":698,"children":1089},[1090,1091,1092,1093],{"id":704,"depth":882,"text":705},{"id":789,"depth":882,"text":790},{"id":812,"depth":882,"text":813},{"id":849,"depth":882,"text":850},{"id":948,"depth":530,"text":949,"children":1095},[1096,1097,1098],{"id":952,"depth":882,"text":396},{"id":972,"depth":882,"text":972},{"id":1003,"depth":882,"text":469},{"id":1016,"depth":530,"text":1017},{"id":1043,"depth":530,"text":1043},{"id":1055,"depth":530,"text":1055},"從 REST constraints 出發，理解 RESTful API 為何這樣設計，以及每個原則在實務開發中的意義與常見錯誤。","md",null,{"tags":1106,"category":203,"date":1112},[1107,1108,1109,1110,1111],"rest","api","backend","architecture","http","2026-02-12",true,{"title":301,"description":1102},"nCG79l8P3Us7TsWMESkZeDTLoekPVtk2OzkvqM64bQY",[1117,1119],{"title":297,"path":298,"stem":299,"description":1118,"children":-1},"深入理解 RESTful API 的核心概念、五大設計原則與實務範例，建立正確的 API 架構思維。",{"title":305,"path":306,"stem":307,"description":1120,"children":-1},"從原理到實戰，完整理解 SSE 的運作機制、資料格式與 Node.js 實作方式，並學會何時選擇 SSE 而非 WebSocket。",1776690847737]