REST API設計最佳實務與常見錯誤

更新於 發佈於 閱讀時間約 23 分鐘

在前面的文章中,我們已經詳細介紹了 HTTP 協議,包括它的結構、請求方法、狀態碼等內容。如果你對 HTTP 協議還不夠熟悉,可以回顧之前的篇章。今天,我們將進一步深入探討 REST API 設計,重點關注如何運用 HTTP 協議來構建高效且擴展性強的 API,並介紹一些進階設計原則與實踐。


什麼是 REST API?

REST API(Representational State Transfer Application Programming Interface)是一種基於 HTTP 協議的 API 設計風格。它通過資源導向的設計,使應用之間的數據交換和操作更加簡單和直觀。REST API 的核心是將「資源」作為中心,並充分利用 HTTP 協議的特性(如方法、狀態碼和 Headers)。

簡單來說,REST API 就像是一個針對資源的服務,每個資源都有自己的 URL,而對這些資源的操作則是通過不同的 HTTP 方法來完成的。


REST API 設計的核心原則

設計一個優秀的 REST API 需要遵循一些核心原則,這不僅能保證功能的實現,也有助於保持 API 的一致性、可擴展性和可維護性。


1. 資源導向設計

REST API 的設計應以「資源」為中心,而非行為或操作。每個資源都應該有一個唯一的 URI(統一資源標識符),資源通常是應用中的某個實體(如「用戶」或「訂單」)。設計時,應通過 URI 來訪問具體資源。

範例:

  • /users:表示所有用戶的資源集合。
  • /users/123:表示 ID 為 123 的具體用戶資源。

這樣的設計使得 API 使用者可以輕易理解 API 的結構,並直觀地知道如何訪問或操作資源。


2. 正確使用 HTTP 方法

HTTP 方法定義了對資源應該進行的操作。在 REST API 中,正確使用這些方法至關重要,因為它們賦予 API 自然且一致的行為。

  • GET:檢索資源,用於從伺服器獲取資料。這是一個安全且無副作用的操作。
  • POST:創建資源,用於向伺服器提交數據並創建新資源。這是有副作用的操作。
  • PUT:更新資源,替換現有資源的整體內容,是冪等操作(多次執行結果一致)。
  • PATCH:部分更新資源,靈活地修改現有資源的一部分。
  • DELETE:刪除資源,移除指定的資源,也是冪等操作。

正確的操作方法可以提升 API 的一致性與可讀性,讓開發者清楚知道每個請求應該如何工作。


3. 無狀態性

REST API 是無狀態的,每一次的請求都是獨立的。伺服器不會保留客戶端的上下文信息,因此每個請求必須包含完成操作所需的所有信息。這種設計讓 API 更容易擴展,因為請求可以分散到不同的伺服器進行處理。

範例:

GET /users/123
Host: api.example.com
Authorization: Bearer <token>

這樣的請求包含了所有必需的信息,伺服器無需依賴之前的請求上下文。


4. 正確使用 HTTP 狀態碼

REST API 通過 HTTP 狀態碼反饋請求結果。正確地使用狀態碼能夠幫助 API 使用者快速了解請求的結果或錯誤原因。

常見狀態碼:

  • 200 OK:請求成功,並返回資源。
  • 201 Created:創建成功(適用於 POST 請求)。
  • 204 No Content:請求成功,但無需返回資源(適用於 DELETE 請求)。
  • 400 Bad Request:無效請求。
  • 401 Unauthorized:未經授權,請求需要身份驗證。
  • 404 Not Found:資源不存在。
  • 500 Internal Server Error:伺服器內部錯誤。

5. 支持過濾、分頁和排序

當 API 返回大量數據時,應該支持過濾、分頁和排序,這不僅提高了效率,也使得客戶端能夠靈活地篩選和處理數據。

範例:

GET /users?age=25&limit=10&page=2&sort=created_at

這樣的請求會過濾出年齡為 25 的用戶,並按創建時間排序,返回第二頁的 10 個結果。


6. 避免過度設計

REST API 設計應保持簡潔和易於使用。過度設計會讓 API 變得難以理解和維護。應避免在 URI 中混入動詞或具體操作,如 /getAllUsers/deleteUserById。這些操作應通過 HTTP 方法來實現,而非通過 URI。


進階設計:版本控制與擴展性

隨著 API 的演變,應用需求的變化可能會影響 API 的設計。因此,版本控制和擴展性在設計過程中是不可忽視的要素。


1. 版本控制

API 需要隨著時間推進進行升級或變更,因此保持對現有使用者的兼容性至關重要。常見的版本控制方法包括:

  • URI 版本控制:如 /v1/users
  • Header 版本控制:通過 HTTP Header 指定版本號,如 Accept: application/vnd.example.v1+json
  • 參數版本控制:使用查詢參數來指定版本,如 /users?version=1

在實際應用中,URI 版本控制最為常見和直觀。


2. 向後兼容與擴展性

在設計 API 時,要盡量避免對現有字段的移除或修改。應通過新增字段或路由來進行擴展,以保證對現有使用者的兼容性。

範例:

{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"new_field": "new_value" // 新增字段,保持向後兼容
}

這樣設計的 API 可以保持現有功能不變,並允許新需求的擴展。


不好的 REST API 設計範例

以下是一個設計不佳的 REST API 範例,並解釋了其中的問題:

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private static List<User> Users = new List<User>
{
new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 },
new User { Id = 2, Name = "Jane Smith", Email = "jane@example.com", Age = 25 }
};

// GET: api/users
[HttpGet("action")]//設計錯誤: 違背了資源導向的設計原則
public ActionResult<IEnumerable<User>> GetAllUsers()
{
return Ok(Users);
}

// POST: api/users/action/add
[HttpPost("action/add")]//設計錯誤: 違背了資源導向的設計原則
public ActionResult<User> CreateNewUser([FromBody] User newUser)
{
newUser.Id = Users.Max(u => u.Id) + 1;
Users.Add(newUser);
return Ok(newUser); // 設計錯誤:應該返回 201 Created
}

// PUT: api/users/action/update/{id}
[HttpPut("action/update/{id}")]//設計錯誤: 違背了資源導向的設計原則
public ActionResult UpdateUserDetails(int id, [FromBody] User updatedUser)
{
var user = Users.FirstOrDefault(u => u.Id == id);
if (user == null)
{
return NotFound();
}
user.Name = updatedUser.Name;
user.Email = updatedUser.Email;
user.Age = updatedUser.Age;
return Ok(user); // 設計錯誤:應該返回 204 No Content
}

// DELETE: api/users/action/delete/{id}
[HttpDelete("action/delete/{id}")]//設計錯誤: 違背了資源導向的設計原則
public ActionResult RemoveUser(int id)
{
var user = Users.FirstOrDefault(u => u.Id == id);
if (user == null)
{
return NotFound();
}
Users.Remove(user);
return Ok(); // 設計錯誤:應該返回 204 No Content
}

// 自定義操作方法,存在設計缺陷
[HttpPost("custom-action")] // 設計錯誤:不明確的操作,無法清楚理解該操作的目的
public ActionResult CustomOperation([FromBody] CustomRequest request)
{
// 根據 OperationType 字段來決定具體的操作
if (request.OperationType == "updateEmail")
{
var user = Users.FirstOrDefault(u => u.Id == request.UserId);
if (user == null)
{
return NotFound("User not found");
}
user.Email = request.Data; // 更新用戶的電子郵件
}
else if (request.OperationType == "deleteUser")
{
var user = Users.FirstOrDefault(u => u.Id == request.UserId);
if (user == null)
{
return NotFound("User not found");
}
Users.Remove(user); // 刪除用戶
}
else if (request.OperationType == "addNote")
{
var user = Users.FirstOrDefault(u => u.Id == request.UserId);
if (user == null)
{
return NotFound("User not found");
}
// 為用戶添加備註(在現有的資料上增加新數據)
// 假設用戶模型中存在 Notes 屬性
user.Notes.Add(request.Data);
}
else
{
return BadRequest("Unknown operation");
}

// 無論進行什麼操作,最終都返回相同的回應
return Ok("Custom operation performed");
}
}

public class CustomRequest
{
public string OperationType { get; set; }
public int UserId { get; set; }
public string Data { get; set; }
}

為什麼這個設計不佳?

  1. 混亂的 URI 設計
    • URI 包含 action 和其他具體操作動詞,如 action/add,這違背了資源導向的設計原則。應將 URI 專注於資源,而非操作行為。
    • 改進建議:應使用 /users 來表示所有用戶,使用 /users/{id} 來表示具體用戶,而不是 action/add 等不清晰的操作。
  2. 不正確的 HTTP 方法使用
    • 在 CreateNewUser 和 UpdateUserDetails 中,錯誤地使用了 200 OK 作為回應。正確的設計應該是使用 201 Created 和 204 No Content。
    • 改進建議:對於創建操作,應使用 201 Created,並返回新資源的 URI;對於更新操作,應使用 204 No Content。
  3. 錯誤的狀態碼返回
    • 在刪除和更新操作中返回了 200 OK,而不是更合適的 204 No Content,這讓使用者無法準確理解操作結果。
  4. 不明確的操作
    • CustomOperation 使用 OperationType 來決定執行的邏輯,這違反了單一職責原則,增加了 API 的複雜度,讓使用者難以理解具體的操作內容。
    • 改進建議:將不同的操作分開,給每個操作設計專門的 API 端點,而非依賴於操作參數。

針對第4點「不明確的操作」特別說明~為什麼這樣的設計是錯誤的?

  1. 操作多樣且含混不清
    • 這個 API 端點 custom-action 允許一個請求執行多種不同的操作,例如更新用戶的電子郵件、刪除用戶、或添加備註。
    • 當 API 使用者看到這個端點時,他們無法一眼確定它會執行什麼操作,因為具體的行為是由 OperationType 字段決定的。這與 REST API 的設計原則背道而馳,因為 REST API 強調「資源導向」,每個操作應該有一個明確且一致的 URI 和 HTTP 方法。
  2. 多個行為綁定在一個端點上
    • 在同一個端點中處理多種不相關的行為,違反了單一職責原則。API 使用者需要理解並處理多種操作類型,這增加了使用 API 的複雜度。
    • 例如,updateEmail 應該有一個獨立的端點,如 PUT /users/{id}/email,而 deleteUser 應該對應 DELETE /users/{id},這樣使用者可以直觀地知道每個端點具體執行什麼操作。
  3. 返回一致但無意義的結果
    • 無論請求進行了什麼操作,最後總是返回相同的回應 "Custom operation performed"。這並沒有提供足夠的上下文來告訴客戶端實際完成了什麼操作。如果發生錯誤或某些操作部分完成,用戶將無法得知具體情況。
  4. 不合適的狀態碼使用
    • 這個 API 端點對於每一個操作,無論是更新、刪除還是添加,最終都會返回 200 OK。然而,正確的做法是針對不同操作使用相應的狀態碼。例如: 對於更新操作,應返回 204 No Content。 對於創建新資源或數據,應返回 201 Created。 對於刪除操作,應返回 204 No Content。

改進後的 REST API 設計範例

[ApiController] 
[Route("api/[controller]")] // 定義這個控制器的路由,"api/[controller]" 會自動將控制器名稱替換為 "users"
public class UsersController : ControllerBase
{
// 模擬用戶資料的靜態列表
private static List<User> Users = new List<User>
{
new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 }, // 預設用戶 1
new User { Id = 2, Name = "Jane Smith", Email = "jane@example.com", Age = 25 } // 預設用戶 2
};

// GET: api/users
[HttpGet] // 定義 GET 請求,用於獲取所有用戶資料
public ActionResult<IEnumerable<User>> GetUsers()
{
// 返回 200 OK 狀態碼,並傳回用戶列表
return Ok(Users);
}

// POST: api/users
[HttpPost] // 定義 POST 請求,用於創建新的用戶
public ActionResult<User> CreateUser([FromBody] User newUser)
{
// 分配新用戶的 ID,使用現有用戶的最大 ID 加 1
newUser.Id = Users.Max(u => u.Id) + 1;
// 將新用戶加入用戶列表
Users.Add(newUser);
// 返回 201 Created 狀態碼,並附上新創建用戶
return CreatedAtAction(nameof(GetUsers), new { id = newUser.Id }, newUser);
}

// PUT: api/users/{id}
[HttpPut("{id}")] // 定義 PUT 請求,用於更新指定 ID 的用戶資料
public ActionResult UpdateUser(int id, [FromBody] User updatedUser)
{
// 根據 ID 查找現有用戶
var user = Users.FirstOrDefault(u => u.Id == id);
// 如果用戶不存在,返回 404 Not Found
if (user == null)
{
return NotFound();
}
// 更新用戶的資料
user.Name = updatedUser.Name;
user.Email = updatedUser.Email;
user.Age = updatedUser.Age;
// 返回 204 No Content,表示更新成功但不需要回傳內容
return NoContent();
}

// DELETE: api/users/{id}
[HttpDelete("{id}")] // 定義 DELETE 請求,用於刪除指定 ID 的用戶
public ActionResult DeleteUser(int id)
{
// 根據 ID 查找現有用戶
var user = Users.FirstOrDefault(u => u.Id == id);
// 如果用戶不存在,返回 404 Not Found
if (user == null)
{
return NotFound();
}
// 從列表中移除用戶
Users.Remove(user);
// 返回 204 No Content,表示刪除成功但不需要回傳內容
return NoContent();
}
}

每日小結

設計一個優秀的 REST API 不僅需要實現功能,還必須著眼於 API 的可用性、可維護性和擴展性。堅持資源導向設計、正確使用 HTTP 方法和狀態碼、保持無狀態性、並考慮版本控制和擴展性,這些設計原則能讓 API 更加一致、易於使用且具備彈性。通過避免常見的設計錯誤,我們可以提升 API 的品質,讓使用者在開發和使用過程中獲得更好的體驗。

avatar-img
0會員
12內容數
歡迎來到 ChiYu Code Journey!這裡是我分享技術心得與開發經驗的空間,主要內容涵蓋 C#、.Net、API 開發及雲端等程式主題。偶爾也會分享一些日常生活點滴,像是我與我家可愛的法鬥相處的趣事等。希望在這裡能和大家一起學習、交流,一同踏上這段程式旅程!
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
ChiYu Code Journey 的其他內容
本文探討三種主流 API 架構設計:REST、gRPC 和 GraphQL,比較它們的優缺點及適用場景,並提供 SEO 建議,以提升文章的搜尋引擎排名。
本文詳細闡述如何建立強大的API模型,涵蓋API需求分析、資源定義、資源層級架構、操作事件設計、流程補充,以及時序圖驗證等步驟,旨在確保API滿足業務需求並具備良好的可維護性和擴展性。文章以圖書館管理系統為例,逐步說明API建模流程,並解釋HTTP方法和API安全特性在API設計中的重要性。
本文探討程式設計下的API模型設計,特別是Resource-Based API的概念,強調區分「資料」與「資源」的重要性,避免直接暴露資料庫原始資料。文中介紹MVC和MVVM架構,說明如何透過模型過濾、轉換資料,保護敏感數據並提升API靈活性及可維護性。
本文討論 API 設計中界定 API 邊界的重要性,說明如何避免多合一 API 的缺點,並透過理解業務需求、識別核心資源和劃分功能責任等步驟,設計出清晰、高效且易於維護的 API。文章以圖書館管理系統為例,說明如何界定 API 邊界,並說明正確使用 HTTP 方法和狀態碼的重要性。
本文深入探討 Web API 與 HTTP 協議,解釋 HTTP 請求方法 (GET, POST, PUT, PATCH, DELETE)、HTTP 結構 (Headers, Body, 狀態碼),。透過說明各種 HTTP 狀態碼,讀者可以更深入理解 Web API 的設計與應用。
本篇文章淺顯易懂地介紹什麼是API(應用程式介面),並以生活化的例子和C#程式碼範例說明介面的概念,以及API在不同領域的應用和優勢,例如Web API、作業系統API、庫或框架API等,並點出其在社群媒體整合、支付系統、地圖服務等日常生活中的重要性。
本文探討三種主流 API 架構設計:REST、gRPC 和 GraphQL,比較它們的優缺點及適用場景,並提供 SEO 建議,以提升文章的搜尋引擎排名。
本文詳細闡述如何建立強大的API模型,涵蓋API需求分析、資源定義、資源層級架構、操作事件設計、流程補充,以及時序圖驗證等步驟,旨在確保API滿足業務需求並具備良好的可維護性和擴展性。文章以圖書館管理系統為例,逐步說明API建模流程,並解釋HTTP方法和API安全特性在API設計中的重要性。
本文探討程式設計下的API模型設計,特別是Resource-Based API的概念,強調區分「資料」與「資源」的重要性,避免直接暴露資料庫原始資料。文中介紹MVC和MVVM架構,說明如何透過模型過濾、轉換資料,保護敏感數據並提升API靈活性及可維護性。
本文討論 API 設計中界定 API 邊界的重要性,說明如何避免多合一 API 的缺點,並透過理解業務需求、識別核心資源和劃分功能責任等步驟,設計出清晰、高效且易於維護的 API。文章以圖書館管理系統為例,說明如何界定 API 邊界,並說明正確使用 HTTP 方法和狀態碼的重要性。
本文深入探討 Web API 與 HTTP 協議,解釋 HTTP 請求方法 (GET, POST, PUT, PATCH, DELETE)、HTTP 結構 (Headers, Body, 狀態碼),。透過說明各種 HTTP 狀態碼,讀者可以更深入理解 Web API 的設計與應用。
本篇文章淺顯易懂地介紹什麼是API(應用程式介面),並以生活化的例子和C#程式碼範例說明介面的概念,以及API在不同領域的應用和優勢,例如Web API、作業系統API、庫或框架API等,並點出其在社群媒體整合、支付系統、地圖服務等日常生活中的重要性。
你可能也想看
Google News 追蹤
Thumbnail
現代社會跟以前不同了,人人都有一支手機,只要打開就可以獲得各種資訊。過去想要辦卡或是開戶就要跑一趟銀行,然而如今科技快速發展之下,金融App無聲無息地進到你生活中。但同樣的,每一家銀行都有自己的App時,我們又該如何選擇呢?(本文係由國泰世華銀行邀約) 今天我會用不同角度帶大家看這款國泰世華CUB
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
※ 什麼是Web API API 就是後端開出來讓前端來用的介面,讓前端與後端可以溝通。 API流程: 終端使用者用任何一種裝置進入瀏覽器。 瀏覽器透過 API 向後端發出請求,請求查詢或修改資料。 後端透過 API 收到前端的請求後,取得資料並回應給前端。 前端渲染畫面,終端使用者
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
xhr 在下面的例子裡,我們首先建立了一個 XMLHttpRequest 物件,並使用 .open() 開啟一個 URL,最後使用 .send() 發出 request。 具體來說步驟有四個: 建立XMLHttpReque 開啟一個請求。 送出請求。 拿到回應後去處理畫面要如何呈現。
※ 什麼是 RESTful API? 這種運用 HTTP 來表達語義的路由設計風格稱為 RESTful API,它描述了如何實現 Web API 的架構。所謂的 API 是應用程式介面 (application programming interface),網址也是一種應用程式的「介面」,故稱為
Thumbnail
在API介接中使用x-www-form-urlencoded格式時,可能會遇到一些踩坑的情況,本文分享了作者在這方面遇到的問題和解決方法。
Thumbnail
在開發前後端分離架構時,使用兩個不同網域所遇到跨域請求問題。特別是在POST請求時行為差異大,揭示了「簡單請求」與「預檢請求」的關鍵差異。簡單請求不需預檢,但application/json會觸發預檢請求,需透過特定設定解決。分享這篇文章希望幫助開發者有效處理跨域問題。
Thumbnail
當這產品的這個 API 被呼叫,再從回傳內容的某個欄位欄位來判斷,只要“這個欄位”顯示 false 就代表不支援」,雖然這樣的設計也能滿足功能需求…
Thumbnail
先前幾篇筆記介紹了網路請求,瀏覽器儲存資料的方式,那麼實務上,前端最常需要發送網路請求的時候,就是透過呼叫 API,去向後端工程師發送/請求資料,所以今天來記錄什麼是 API吧!
Thumbnail
Request內容 package main import ( "fmt" "log" "net/http" "strings" ) func request(w http.ResponseWriter, r *http.Request) { //這些資訊是輸出到伺服器端的列印訊息
Thumbnail
Accept:用戶端能夠接收的內容類型。 Accept: text/plain, text/html Accept-Charset:瀏覽器可以接受的字元編碼集。 Accept-Charset: utf8 Accept-Encoding:指定瀏覽器可以支援的web伺服器返回內容壓縮編碼
Thumbnail
現代社會跟以前不同了,人人都有一支手機,只要打開就可以獲得各種資訊。過去想要辦卡或是開戶就要跑一趟銀行,然而如今科技快速發展之下,金融App無聲無息地進到你生活中。但同樣的,每一家銀行都有自己的App時,我們又該如何選擇呢?(本文係由國泰世華銀行邀約) 今天我會用不同角度帶大家看這款國泰世華CUB
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
※ 什麼是Web API API 就是後端開出來讓前端來用的介面,讓前端與後端可以溝通。 API流程: 終端使用者用任何一種裝置進入瀏覽器。 瀏覽器透過 API 向後端發出請求,請求查詢或修改資料。 後端透過 API 收到前端的請求後,取得資料並回應給前端。 前端渲染畫面,終端使用者
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
xhr 在下面的例子裡,我們首先建立了一個 XMLHttpRequest 物件,並使用 .open() 開啟一個 URL,最後使用 .send() 發出 request。 具體來說步驟有四個: 建立XMLHttpReque 開啟一個請求。 送出請求。 拿到回應後去處理畫面要如何呈現。
※ 什麼是 RESTful API? 這種運用 HTTP 來表達語義的路由設計風格稱為 RESTful API,它描述了如何實現 Web API 的架構。所謂的 API 是應用程式介面 (application programming interface),網址也是一種應用程式的「介面」,故稱為
Thumbnail
在API介接中使用x-www-form-urlencoded格式時,可能會遇到一些踩坑的情況,本文分享了作者在這方面遇到的問題和解決方法。
Thumbnail
在開發前後端分離架構時,使用兩個不同網域所遇到跨域請求問題。特別是在POST請求時行為差異大,揭示了「簡單請求」與「預檢請求」的關鍵差異。簡單請求不需預檢,但application/json會觸發預檢請求,需透過特定設定解決。分享這篇文章希望幫助開發者有效處理跨域問題。
Thumbnail
當這產品的這個 API 被呼叫,再從回傳內容的某個欄位欄位來判斷,只要“這個欄位”顯示 false 就代表不支援」,雖然這樣的設計也能滿足功能需求…
Thumbnail
先前幾篇筆記介紹了網路請求,瀏覽器儲存資料的方式,那麼實務上,前端最常需要發送網路請求的時候,就是透過呼叫 API,去向後端工程師發送/請求資料,所以今天來記錄什麼是 API吧!
Thumbnail
Request內容 package main import ( "fmt" "log" "net/http" "strings" ) func request(w http.ResponseWriter, r *http.Request) { //這些資訊是輸出到伺服器端的列印訊息
Thumbnail
Accept:用戶端能夠接收的內容類型。 Accept: text/plain, text/html Accept-Charset:瀏覽器可以接受的字元編碼集。 Accept-Charset: utf8 Accept-Encoding:指定瀏覽器可以支援的web伺服器返回內容壓縮編碼