02-2. HTTP 請求與回應

⏱️ 閱讀時間: 12 分鐘 🎯 難度: ⭐⭐ (簡單)


🎯 本篇重點

深入理解 HTTP 請求和回應的結構、常用標頭的意義、如何使用開發者工具觀察,以及實際應用範例。


🤔 什麼是 HTTP 請求和回應?

一句話解釋: HTTP 請求就像是「訂餐單」,HTTP 回應就像是「外送餐點」。

你(瀏覽器) → 餐廳(伺服器)

請求(訂餐單):
- 餐廳名稱(Host)
- 餐點名稱(URL)
- 訂餐方式(Method)
- 特殊需求(Headers)
- 備註(Body)

回應(外送餐點):
- 訂單狀態(Status Code)
- 餐點內容(Body)
- 包裝資訊(Headers)

📤 HTTP 請求(Request)結構

完整結構

GET /api/users/123 HTTP/1.1              ← 1️⃣ 請求行(Request Line)
Host: api.example.com                     ← 2️⃣ 標頭(Headers)
User-Agent: Mozilla/5.0
Accept: application/json
Authorization: Bearer TOKEN123
Content-Type: application/json
Connection: keep-alive
                                          ← 3️⃣ 空行(必須)
{"update": "data"}                        ← 4️⃣ 主體(Body,可選)

1️⃣ 請求行(Request Line)

GET /api/users/123 HTTP/1.1
 ↑       ↑           ↑
方法     路徑       版本

組成部分:
1. HTTP 方法(Method)
   - GET, POST, PUT, DELETE...

2. 請求路徑(Request URI)
   - /api/users/123
   - 可以包含查詢參數:/search?q=keyword&page=1

3. HTTP 版本
   - HTTP/1.1(最常用)
   - HTTP/2
   - HTTP/3

2️⃣ 請求標頭(Request Headers)

Host(必須)

Host: www.example.com

說明:
- 指定目標伺服器的域名和埠號
- HTTP/1.1 必須包含
- 支援虛擬主機(一個 IP 多個域名)

範例:
Host: www.example.com
Host: www.example.com:8080

User-Agent

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
            AppleWebKit/537.36 Chrome/120.0.0.0

說明:
- 客戶端的瀏覽器資訊
- 伺服器可以根據不同瀏覽器回傳不同內容

實際用途:
- 行動版網站判斷(iPhone、Android)
- 瀏覽器相容性處理
- 統計分析

範例:
// 桌面版 Chrome
User-Agent: Mozilla/5.0 (Windows NT 10.0) Chrome/120.0

// 行動版 Safari
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)

Accept

Accept: text/html, application/json, */*

說明:
- 客戶端可以接受的內容類型
- 伺服器根據此決定回傳格式

常見值:
Accept: text/html                 - 網頁
Accept: application/json          - JSON 資料
Accept: image/*                   - 任何圖片
Accept: */*                       - 任何類型

多個類型(優先順序):
Accept: text/html, application/json;q=0.9, */*;q=0.8

q=0.9:品質因子(優先順序 0-1)

Authorization

Authorization: Bearer eyJhbGci0iJIUzI1NiIsInR5cCI6IkpXVCJ9...

說明:
- 身份驗證資訊
- 通常用於 API 認證

常見類型:
1. Basic(基本認證)
   Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
   (username:password 的 Base64 編碼)

2. Bearer(Token 認證)
   Authorization: Bearer TOKEN123
   (通常是 JWT)

3. OAuth
   Authorization: OAuth oauth_token="xxx"

Content-Type

Content-Type: application/json; charset=utf-8

說明:
- 請求主體的內容類型
- 只在有 Body 時需要(POST、PUT)

常見值:
Content-Type: application/json           - JSON 資料
Content-Type: application/x-www-form-urlencoded  - 表單
Content-Type: multipart/form-data        - 檔案上傳
Content-Type: text/plain                 - 純文字
Content-Type: text/html                  - HTML

Cookie: sessionid=abc123; user_pref=dark_mode

說明:
- 儲存在客戶端的資料
- 每次請求自動帶上
- 用於保持登入狀態

範例:
Cookie: sessionid=abc123; token=xyz789; lang=zh-tw

Referer

Referer: https://www.google.com/

說明:
- 來源頁面 URL
- 告訴伺服器從哪個頁面連過來的

用途:
- 統計分析(流量來源)
- 防盜連(檢查來源)
- 除錯(追蹤用戶路徑)

3️⃣ 空行

必須有一個空行分隔標頭和主體
即使沒有主體也要有空行

4️⃣ 請求主體(Request Body)

說明:
- 傳送給伺服器的資料
- GET、HEAD、DELETE 通常沒有 Body
- POST、PUT、PATCH 通常有 Body

範例 1:JSON 格式
POST /api/users HTTP/1.1
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john@example.com",
  "age": 30
}

範例 2:表單格式
POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded

username=john&password=123456

範例 3:檔案上傳
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

[二進位圖片資料]
------Boundary--

📥 HTTP 回應(Response)結構

完整結構

HTTP/1.1 200 OK                           ← 1️⃣ 狀態行(Status Line)
Date: Mon, 06 Jan 2025 12:00:00 GMT       ← 2️⃣ 標頭(Headers)
Server: Apache/2.4.41
Content-Type: application/json
Content-Length: 1234
Cache-Control: max-age=3600
Set-Cookie: sessionid=abc123
Connection: keep-alive
                                          ← 3️⃣ 空行(必須)
{                                         ← 4️⃣ 主體(Body)
  "id": 123,
  "name": "John Doe"
}

1️⃣ 狀態行(Status Line)

HTTP/1.1 200 OK
   ↑     ↑   ↑
 版本  狀態碼 原因短語

組成部分:
1. HTTP 版本
   - HTTP/1.1

2. 狀態碼(Status Code)
   - 200(成功)
   - 404(找不到)
   - 500(伺服器錯誤)

3. 原因短語(Reason Phrase)
   - OK
   - Not Found
   - Internal Server Error

2️⃣ 回應標頭(Response Headers)

Content-Type

Content-Type: application/json; charset=utf-8

說明:
- 回應主體的內容類型
- 客戶端根據此解析內容

常見值:
Content-Type: application/json     - JSON 資料
Content-Type: text/html            - HTML 網頁
Content-Type: text/plain           - 純文字
Content-Type: image/jpeg           - JPEG 圖片
Content-Type: application/pdf      - PDF 文件

Content-Length

Content-Length: 1234

說明:
- 回應主體的位元組數
- 客戶端知道要讀取多少資料

範例:
Content-Length: 0        - 沒有內容
Content-Length: 12345    - 12345 bytes

Set-Cookie: sessionid=abc123; HttpOnly; Secure; Max-Age=3600

說明:
- 伺服器設定 Cookie
- 客戶端儲存並在後續請求帶上

屬性:
Set-Cookie: name=value;
            Domain=.example.com;  - 有效域名
            Path=/;               - 有效路徑
            Expires=Wed, 09 Jun 2025 10:18:14 GMT;  - 過期時間
            Max-Age=3600;         - 存活秒數
            HttpOnly;             - 禁止 JavaScript 存取(防 XSS)
            Secure;               - 只在 HTTPS 傳送
            SameSite=Strict       - 防 CSRF 攻擊

範例:
Set-Cookie: sessionid=abc123; HttpOnly; Secure
Set-Cookie: lang=zh-tw; Max-Age=31536000

Cache-Control

Cache-Control: max-age=3600, public

說明:
- 控制快取行為
- 減少重複請求

常見值:
Cache-Control: no-cache            - 每次都重新驗證
Cache-Control: no-store            - 完全不快取
Cache-Control: max-age=3600        - 快取 1 小時
Cache-Control: public              - 可被公共快取(CDN)
Cache-Control: private             - 只能被瀏覽器快取
Cache-Control: must-revalidate     - 過期後必須重新驗證

組合使用:
Cache-Control: public, max-age=31536000
(公共快取,快取 1 年)

Server

Server: Apache/2.4.41 (Unix)

說明:
- 伺服器軟體資訊

常見值:
Server: Apache/2.4.41
Server: nginx/1.20.1
Server: Microsoft-IIS/10.0
Server: cloudflare

Location

Location: https://www.example.com/new-page

說明:
- 重新導向的目標 URL
- 通常搭配 3xx 狀態碼

範例:
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/new-url

HTTP/1.1 302 Found
Location: https://www.example.com/temp-url

Access-Control-Allow-Origin(CORS)

Access-Control-Allow-Origin: *

說明:
- 跨來源資源共用(CORS)
- 允許哪些域名存取資源

常見值:
Access-Control-Allow-Origin: *
(允許所有域名)

Access-Control-Allow-Origin: https://www.example.com
(只允許特定域名)

搭配其他 CORS 標頭:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

4️⃣ 回應主體(Response Body)

說明:
- 伺服器回傳的實際內容
- 可以是 HTML、JSON、圖片等

範例 1:JSON 回應
HTTP/1.1 200 OK
Content-Type: application/json

{
  "status": "success",
  "data": {
    "id": 123,
    "name": "John Doe"
  }
}

範例 2:HTML 回應
HTTP/1.1 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
<head><title>首頁</title></head>
<body>Hello World!</body>
</html>

範例 3:錯誤回應
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "User not found",
  "message": "The requested user does not exist"
}

🔍 完整範例:註冊新用戶

請求(Request)

POST /api/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0)
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJhbGci...
Connection: keep-alive
Content-Length: 123

{
  "username": "john_doe",
  "email": "john@example.com",
  "password": "SecurePass123",
  "age": 30
}

回應(Response)- 成功

HTTP/1.1 201 Created
Date: Mon, 06 Jan 2025 12:00:00 GMT
Server: nginx/1.20.1
Content-Type: application/json
Content-Length: 456
Location: /api/users/123
Set-Cookie: sessionid=abc123; HttpOnly; Secure
Cache-Control: no-store
Connection: keep-alive

{
  "status": "success",
  "message": "User created successfully",
  "data": {
    "id": 123,
    "username": "john_doe",
    "email": "john@example.com",
    "created_at": "2025-01-06T12:00:00Z"
  }
}

回應(Response)- 失敗

HTTP/1.1 400 Bad Request
Date: Mon, 06 Jan 2025 12:00:00 GMT
Server: nginx/1.20.1
Content-Type: application/json
Content-Length: 234

{
  "status": "error",
  "message": "Validation failed",
  "errors": {
    "email": ["Email already exists"],
    "password": ["Password must be at least 8 characters"]
  }
}

💻 使用開發者工具觀察 HTTP

Chrome DevTools

步驟 1:打開開發者工具
- Windows: F12 或 Ctrl+Shift+I
- Mac: Cmd+Option+I

步驟 2:切換到 Network 分頁

步驟 3:重新載入網頁(Ctrl+R / Cmd+R)

步驟 4:點選任一請求,查看詳細資訊

可以看到:
├─ General(概要)
│  ├─ Request URL
│  ├─ Request Method
│  ├─ Status Code
│  └─ Remote Address
│
├─ Request Headers(請求標頭)
│  ├─ Host
│  ├─ User-Agent
│  └─ Cookie
│
├─ Response Headers(回應標頭)
│  ├─ Content-Type
│  ├─ Set-Cookie
│  └─ Cache-Control
│
├─ Request Payload(請求資料)
└─ Response(回應內容)

使用 curl 測試

# 基本 GET 請求
curl https://api.example.com/users/123

# 顯示回應標頭
curl -I https://api.example.com/users/123

# POST 請求(JSON)
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer TOKEN" \
  -d '{"name":"John","email":"john@example.com"}'

# 顯示完整的請求和回應
curl -v https://api.example.com/users/123

# 輸出範例:
> GET /users/123 HTTP/1.1
> Host: api.example.com
> User-Agent: curl/7.68.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 123
<
{"id":123,"name":"John"}

🌐 實際應用場景

場景 1:登入流程

【步驟 1:發送登入請求】
POST /api/login HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "username": "john",
  "password": "123456"
}

【步驟 2:伺服器驗證並回應】
HTTP/1.1 200 OK
Set-Cookie: sessionid=abc123; HttpOnly; Secure
Content-Type: application/json

{
  "status": "success",
  "token": "eyJhbGci...",
  "user": {
    "id": 123,
    "username": "john"
  }
}

【步驟 3:後續請求帶上認證資訊】
GET /api/profile HTTP/1.1
Host: api.example.com
Cookie: sessionid=abc123
Authorization: Bearer eyJhbGci...

【步驟 4:伺服器驗證並回傳資料】
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 123,
  "username": "john",
  "email": "john@example.com"
}

場景 2:檔案上傳

【請求】
POST /api/upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----Boundary123
Content-Length: 123456

------Boundary123
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

[二進位圖片資料]
------Boundary123
Content-Disposition: form-data; name="description"

My vacation photo
------Boundary123--

【回應】
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/files/456

{
  "status": "success",
  "file": {
    "id": 456,
    "filename": "photo.jpg",
    "url": "https://cdn.example.com/photos/456.jpg",
    "size": 123456
  }
}

場景 3:分頁查詢

【請求】
GET /api/articles?page=2&per_page=10&sort=date&order=desc HTTP/1.1
Host: api.example.com
Accept: application/json

【回應】
HTTP/1.1 200 OK
Content-Type: application/json
X-Total-Count: 95
X-Page: 2
X-Per-Page: 10
Link: </api/articles?page=1>; rel="first",
      </api/articles?page=3>; rel="next",
      </api/articles?page=10>; rel="last"

{
  "data": [
    {
      "id": 11,
      "title": "文章標題 11",
      "created_at": "2025-01-05"
    },
    {
      "id": 12,
      "title": "文章標題 12",
      "created_at": "2025-01-04"
    }
    ...
  ],
  "pagination": {
    "current_page": 2,
    "per_page": 10,
    "total": 95,
    "total_pages": 10
  }
}

🎓 面試常見問題

Q1:GET 請求可以有 Body 嗎?

A:理論上可以,但不建議

RFC 7231 規範:
- GET 請求可以包含 Body
- 但語義上 GET 是「取得」資源
- 不應該傳送資料

實際問題:
1. 許多伺服器會忽略 GET 的 Body
2. 中間的代理伺服器可能丟棄 Body
3. 快取機制通常不考慮 Body
4. 瀏覽器和工具可能不支援

正確做法:
- 查詢參數放在 URL:GET /search?q=keyword
- 需要傳送資料用 POST

錯誤示範:
GET /search HTTP/1.1
{"query": "keyword"}  ← 不建議

正確示範:
GET /search?q=keyword HTTP/1.1

Q2:Content-Type 和 Accept 的差異?

A:方向相反

Content-Type(我傳送的內容類型):
- 請求:告訴伺服器「我傳的是什麼格式」
- 回應:告訴客戶端「我回傳的是什麼格式」

Accept(我想要的內容類型):
- 請求:告訴伺服器「我想要什麼格式」
- 回應:(不使用)

範例 1:POST 請求
POST /api/users HTTP/1.1
Content-Type: application/json    ← 我傳送的是 JSON
Accept: application/json           ← 我想要收到 JSON

{"name": "John"}

HTTP/1.1 201 Created
Content-Type: application/json    ← 我回傳的是 JSON

{"id": 123, "name": "John"}

範例 2:內容協商
GET /api/users/123 HTTP/1.1
Accept: application/json          ← 我想要 JSON

HTTP/1.1 200 OK
Content-Type: application/json    ← 伺服器回傳 JSON

GET /api/users/123 HTTP/1.1
Accept: application/xml           ← 我想要 XML

HTTP/1.1 200 OK
Content-Type: application/xml     ← 伺服器回傳 XML

Q3:Cookie 和 Session 的差異?

A:儲存位置不同

Cookie(儲存在客戶端):
- 資料存在瀏覽器
- 每次請求自動帶上
- 有大小限制(4KB)
- 可以設定過期時間
- 可能被竄改(不安全)

範例:
Set-Cookie: user_pref=dark_mode; Max-Age=31536000
Cookie: user_pref=dark_mode

Session(儲存在伺服器):
- 資料存在伺服器
- 只在伺服器端儲存 Session ID
- 無大小限制
- 更安全(資料不外洩)

運作流程:
1. 用戶登入成功
2. 伺服器建立 Session
   - Session ID: abc123
   - Session 資料: {user_id: 123, name: "John"}

3. 伺服器回傳 Session ID(透過 Cookie)
   Set-Cookie: sessionid=abc123

4. 客戶端後續請求帶上 Cookie
   Cookie: sessionid=abc123

5. 伺服器根據 Session ID 查詢 Session 資料
   abc123 → {user_id: 123, name: "John"}

優缺點:
Cookie:
✅ 減輕伺服器負擔
❌ 不安全(可被竄改)
❌ 有大小限制

Session:
✅ 安全(資料在伺服器)
✅ 無大小限制
❌ 增加伺服器負擔
❌ 難以擴展(分散式系統)

Q4:如何處理 CORS 錯誤?

A:伺服器端設定 CORS 標頭

什麼是 CORS?
- Cross-Origin Resource Sharing(跨來源資源共用)
- 瀏覽器安全機制
- 防止惡意網站存取其他網站的資料

錯誤訊息:
Access to fetch at 'https://api.example.com' from origin
'https://www.mysite.com' has been blocked by CORS policy

原因:
- 瀏覽器發現是跨域請求
- 伺服器沒有回傳 CORS 標頭
- 瀏覽器阻擋回應

解決方法(伺服器端):

方法 1:允許所有域名
Access-Control-Allow-Origin: *

方法 2:允許特定域名
Access-Control-Allow-Origin: https://www.mysite.com

方法 3:完整設定
Access-Control-Allow-Origin: https://www.mysite.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400

Python (Flask) 範例:
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app, origins="https://www.mysite.com")

Node.js (Express) 範例:
const cors = require('cors');
app.use(cors({
  origin: 'https://www.mysite.com'
}));

注意:
- CORS 是瀏覽器的限制
- curl、Postman 不受影響
- 開發時可以暫時關閉瀏覽器 CORS 檢查(不建議)

Q5:Authorization 標頭有哪些常見方式?

A:主要有 Basic、Bearer、OAuth

1. Basic Authentication(基本認證)
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

編碼方式:
Base64(username:password)

範例:
username: john
password: secret123
→ john:secret123
→ Base64 編碼
→ am9objpzZWNyZXQxMjM=

缺點:
❌ 不安全(只是編碼,不是加密)
❌ 必須搭配 HTTPS
❌ 每次請求都要傳送密碼

2. Bearer Token(Token 認證)
Authorization: Bearer eyJhbGci0iJIUzI1NiIsInR5cCI6IkpXVCJ9...

流程:
1. 用戶登入 → 伺服器回傳 Token(通常是 JWT)
2. 客戶端儲存 Token
3. 後續請求帶上 Token

優點:
✅ 不用每次傳送密碼
✅ Token 可以設定過期時間
✅ Token 可以包含用戶資訊(JWT)

JWT 結構:
eyJhbGci... .eyJ1c2VyX... .SflKxwRJ...
   ↑            ↑             ↑
 Header       Payload      Signature
 (演算法)    (用戶資料)      (簽名)

3. OAuth 2.0
Authorization: Bearer ACCESS_TOKEN

流程:
1. 用戶授權(例:用 Google 登入)
2. 取得 Access Token
3. 使用 Access Token 存取 API

範例:
Authorization: Bearer ya29.a0AfH6SMBx...

實務建議:
- API 認證:用 Bearer Token(JWT)
- 第三方登入:用 OAuth 2.0
- 內部系統:可以用 Basic(搭配 HTTPS)
- 避免在 URL 傳送 Token(會被記錄)

✅ 重點回顧

HTTP 請求結構:

請求行 + 標頭 + 空行 + 主體

HTTP 回應結構:

狀態行 + 標頭 + 空行 + 主體

重要請求標頭:

  • Host - 目標伺服器(必須)
  • User-Agent - 客戶端資訊
  • Accept - 接受的內容類型
  • Authorization - 身份驗證
  • Content-Type - 請求內容類型
  • Cookie - 儲存的資料

重要回應標頭:

  • Content-Type - 回應內容類型
  • Set-Cookie - 設定 Cookie
  • Cache-Control - 快取控制
  • Location - 重新導向目標
  • Access-Control-Allow-Origin - CORS 設定

面試重點:

  • ✅ GET 不建議有 Body
  • ✅ Content-Type(我傳的)vs Accept(我要的)
  • ✅ Cookie(客戶端)vs Session(伺服器)
  • ✅ CORS 需要伺服器設定標頭
  • ✅ Authorization 有 Basic、Bearer、OAuth

上一篇: 02-1. HTTP 基礎概念 下一篇: 02-3. HTTP 方法(GET、POST、PUT、DELETE)


最後更新:2025-01-06

0%