02-4. HTTP 狀態碼完整指南

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


🎯 本篇重點

完整理解 HTTP 狀態碼的分類、常見狀態碼的意義、使用場景,以及如何在實際開發中正確使用。


🤔 什麼是 HTTP 狀態碼?

HTTP Status Code = 伺服器告訴客戶端「請求處理結果」的代碼

一句話解釋: HTTP 狀態碼就像是餐廳服務生回應你訂餐的狀態:「好的」(200)、「找不到這道菜」(404)、「廚房壞了」(500)。


🍽️ 用餐廳點餐來比喻狀態碼

你(客戶端) → 服務生(伺服器)

【2xx - 成功】
你:我要一份牛排
服務生:好的,馬上來!(200 OK)
服務生:已為您建立訂單(201 Created)

【3xx - 重新導向】
你:我要招牌菜
服務生:招牌菜改名了,現在叫特選牛排(301 Moved Permanently)
服務生:今天招牌菜換成豬排了(302 Found)

【4xx - 客戶端錯誤】
你:我要一份恐龍肉
服務生:我們沒有這道菜(404 Not Found)
你:我要一杯酒
服務生:您未滿 18 歲,無法提供(403 Forbidden)
你:(亂吼)
服務生:聽不懂您說什麼(400 Bad Request)

【5xx - 伺服器錯誤】
你:我要一份牛排
服務生:廚房壞了(500 Internal Server Error)
服務生:廚師請假,暫停營業(503 Service Unavailable)

📊 HTTP 狀態碼分類

五大類

1xx - 資訊性回應(Informational)
├─ 請求已收到,繼續處理
└─ 較少使用

2xx - 成功(Success)
├─ 請求成功處理
└─ 最常見的是 200 OK

3xx - 重新導向(Redirection)
├─ 需要進一步操作
└─ 瀏覽器會自動處理

4xx - 客戶端錯誤(Client Error)
├─ 請求有錯誤
├─ 客戶端的問題
└─ 最常見的是 404 Not Found

5xx - 伺服器錯誤(Server Error)
├─ 伺服器處理失敗
├─ 伺服器的問題
└─ 最常見的是 500 Internal Server Error

1️⃣ 1xx - 資訊性回應

100 Continue

說明:
- 客戶端可以繼續發送請求
- 伺服器已收到請求的初始部分

使用場景:
- 上傳大檔案時
- 客戶端先發送 Expect: 100-continue 標頭
- 伺服器確認可以接收後回傳 100

範例:
【客戶端】
POST /upload HTTP/1.1
Host: api.example.com
Content-Length: 1073741824
Expect: 100-continue

【伺服器】
HTTP/1.1 100 Continue

【客戶端】
[開始上傳 1GB 的檔案資料...]

用途:
避免上傳大檔案後才發現伺服器拒絕

101 Switching Protocols

說明:
- 切換協定
- 伺服器同意切換到客戶端請求的協定

使用場景:
- WebSocket 升級

範例:
【客戶端】
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade

【伺服器】
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

→ 從 HTTP 切換到 WebSocket

2️⃣ 2xx - 成功

200 OK

說明:
- 請求成功
- 最常見的狀態碼

使用場景:
- GET 請求成功取得資源
- POST/PUT/PATCH 請求成功且有回傳內容

範例 1:GET 請求
【請求】
GET /api/users/123 HTTP/1.1

【回應】
HTTP/1.1 200 OK
Content-Type: application/json

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

範例 2:POST 請求
【請求】
POST /api/login HTTP/1.1
Content-Type: application/json

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

【回應】
HTTP/1.1 200 OK
Content-Type: application/json

{
  "token": "eyJhbGci...",
  "user": {"id": 123, "name": "John"}
}

201 Created

說明:
- 資源建立成功
- 通常用於 POST 請求

使用場景:
- POST 建立新資源

範例:
【請求】
POST /api/users HTTP/1.1
Content-Type: application/json

{
  "username": "alice",
  "email": "alice@example.com"
}

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

{
  "id": 456,
  "username": "alice",
  "email": "alice@example.com",
  "created_at": "2025-01-06T12:00:00Z"
}

重點:
- 回傳 Location 標頭(新資源的 URL)
- 回應內容包含新建立的資源

204 No Content

說明:
- 請求成功,但沒有回傳內容
- 通常用於 DELETE 或 PUT 請求

使用場景:
- DELETE 刪除成功
- PUT/PATCH 更新成功但不需回傳資料

範例 1:DELETE
【請求】
DELETE /api/users/123 HTTP/1.1

【回應】
HTTP/1.1 204 No Content

(沒有 Body)

範例 2:PUT
【請求】
PUT /api/users/123 HTTP/1.1
Content-Type: application/json

{"name": "John Updated"}

【回應】
HTTP/1.1 204 No Content

(更新成功,但不回傳更新後的資料)

206 Partial Content

說明:
- 部分內容
- 用於斷點續傳

使用場景:
- 下載大檔案
- 影片串流

範例:
【請求】
GET /video.mp4 HTTP/1.1
Range: bytes=0-1023

【回應】
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/1073741824
Content-Length: 1024

[前 1KB 的資料]

用途:
- 支援續傳(下載中斷後可從斷點繼續)
- 影片播放器可以只載入需要的部分

3️⃣ 3xx - 重新導向

301 Moved Permanently

說明:
- 永久重新導向
- 資源永久移到新位置

使用場景:
- 網站改版,URL 結構改變
- 搜尋引擎會更新索引

範例:
【請求】
GET /old-page HTTP/1.1
Host: www.example.com

【回應】
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/new-page

→ 瀏覽器自動跳轉到新 URL
→ 搜尋引擎更新索引

實際應用:
- HTTP 強制跳轉到 HTTPS
- 舊網址重新導向到新網址

302 Found

說明:
- 暫時重新導向
- 資源暫時在另一個位置

使用場景:
- 臨時維護
- A/B 測試

範例:
【請求】
GET /promotion HTTP/1.1

【回應】
HTTP/1.1 302 Found
Location: /special-offer

→ 瀏覽器跳轉到臨時網址
→ 搜尋引擎不更新索引

301 vs 302:
301(永久):
- 搜尋引擎更新索引
- 瀏覽器可能快取
- 用於永久性的改變

302(暫時):
- 搜尋引擎不更新索引
- 瀏覽器不快取
- 用於臨時性的重新導向

304 Not Modified

說明:
- 資源未修改
- 可以使用快取

使用場景:
- 快取驗證

範例:
【第一次請求】
GET /api/articles/123 HTTP/1.1

【回應】
HTTP/1.1 200 OK
Last-Modified: Mon, 06 Jan 2025 12:00:00 GMT
ETag: "abc123"

{文章內容...}

【第二次請求(帶上快取資訊)】
GET /api/articles/123 HTTP/1.1
If-Modified-Since: Mon, 06 Jan 2025 12:00:00 GMT
If-None-Match: "abc123"

【回應】
HTTP/1.1 304 Not Modified

→ 客戶端使用快取的內容
→ 節省頻寬

優點:
- 減少資料傳輸
- 提升載入速度
- 降低伺服器負擔

307 Temporary Redirect

說明:
- 暫時重新導向(保持 HTTP 方法)

302 vs 307:
302:瀏覽器可能把 POST 改成 GET
307:保證保持原本的 HTTP 方法

範例:
【請求】
POST /api/submit HTTP/1.1
Body: {...}

【302 回應】
HTTP/1.1 302 Found
Location: /api/new-submit

→ 瀏覽器可能改用 GET 請求新 URL(錯誤)

【307 回應】
HTTP/1.1 307 Temporary Redirect
Location: /api/new-submit

→ 瀏覽器仍用 POST 請求新 URL(正確)

308 Permanent Redirect

說明:
- 永久重新導向(保持 HTTP 方法)

301 vs 308:
301:瀏覽器可能把 POST 改成 GET
308:保證保持原本的 HTTP 方法

範例:
POST /api/old-endpoint HTTP/1.1

【308 回應】
HTTP/1.1 308 Permanent Redirect
Location: /api/new-endpoint

→ 瀏覽器用 POST 請求新 URL
→ 搜尋引擎更新索引

4️⃣ 4xx - 客戶端錯誤

400 Bad Request

說明:
- 請求格式錯誤
- 伺服器無法理解請求

使用場景:
- JSON 格式錯誤
- 缺少必要欄位
- 資料類型錯誤

範例 1:JSON 格式錯誤
【請求】
POST /api/users HTTP/1.1
Content-Type: application/json

{name: "John"}  ← 缺少引號

【回應】
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "Invalid JSON format"
}

範例 2:缺少必要欄位
【請求】
POST /api/users HTTP/1.1
Content-Type: application/json

{
  "username": "john"
}  ← 缺少 email

【回應】
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "Validation failed",
  "details": {
    "email": ["Email is required"]
  }
}

401 Unauthorized

說明:
- 未認證(需要登入)
- 缺少或無效的認證資訊

使用場景:
- 未登入
- Token 過期
- Token 無效

範例 1:未登入
【請求】
GET /api/profile HTTP/1.1

【回應】
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="API"
Content-Type: application/json

{
  "error": "Authentication required",
  "message": "Please provide a valid token"
}

範例 2:Token 過期
【請求】
GET /api/profile HTTP/1.1
Authorization: Bearer expired_token

【回應】
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "error": "Token expired",
  "message": "Please login again"
}

注意:
401 的英文是 "Unauthorized"
但實際意義是 "Unauthenticated"(未認證)
不是「未授權」

403 Forbidden

說明:
- 已認證但無權限
- 伺服器理解請求但拒絕執行

使用場景:
- 沒有權限存取資源
- IP 被封鎖
- 帳號被停用

範例 1:權限不足
【請求】
DELETE /api/users/123 HTTP/1.1
Authorization: Bearer user_token

【回應】
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": "Insufficient permissions",
  "message": "Only admins can delete users"
}

範例 2:存取他人資料
【請求】
GET /api/users/456/private-info HTTP/1.1
Authorization: Bearer user_123_token

【回應】
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": "Access denied",
  "message": "You can only access your own private information"
}

401 vs 403:
401(Unauthorized):
- 未認證(沒登入或 Token 無效)
- 提供正確認證後可能成功
- 回應標頭包含 WWW-Authenticate

403(Forbidden):
- 已認證但權限不足
- 即使認證也無法存取
- 不包含 WWW-Authenticate

404 Not Found

說明:
- 找不到資源
- 最常見的錯誤之一

使用場景:
- URL 錯誤
- 資源不存在
- 資源已被刪除

範例:
【請求】
GET /api/users/999 HTTP/1.1

【回應】
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "Not found",
  "message": "User with ID 999 does not exist"
}

實際應用:
// 網頁 404 頁面
HTTP/1.1 404 Not Found
Content-Type: text/html

<!DOCTYPE html>
<html>
<head><title>404 - Page Not Found</title></head>
<body>
  <h1>找不到頁面</h1>
  <p>您要訪問的頁面不存在</p>
  <a href="/">回首頁</a>
</body>
</html>

// API 404 回應
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "Resource not found",
  "path": "/api/articles/123",
  "timestamp": "2025-01-06T12:00:00Z"
}

405 Method Not Allowed

說明:
- HTTP 方法不被允許
- 資源存在但不支援此方法

使用場景:
- 對唯讀資源使用 POST/PUT/DELETE

範例:
【請求】
DELETE /api/public-info HTTP/1.1

【回應】
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json

{
  "error": "Method not allowed",
  "message": "This resource is read-only"
}

重點:
- 回應標頭包含 Allow(列出支援的方法)
- 資源存在,但不支援該 HTTP 方法

409 Conflict

說明:
- 請求衝突
- 資源狀態衝突

使用場景:
- 併發更新衝突
- 重複建立資源
- 資源狀態不允許操作

範例 1:重複註冊
【請求】
POST /api/users HTTP/1.1
Content-Type: application/json

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

【回應】
HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "error": "Conflict",
  "message": "Username 'john' already exists"
}

範例 2:狀態衝突
【請求】
POST /api/orders/123/cancel HTTP/1.1

【回應】
HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "error": "Conflict",
  "message": "Cannot cancel order that has already been shipped"
}

429 Too Many Requests

說明:
- 請求過於頻繁
- 超過速率限制(Rate Limit)

使用場景:
- API 速率限制
- 防止 DDoS 攻擊
- 防止暴力破解

範例:
【請求】
GET /api/users HTTP/1.1

【回應】
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704542400
Content-Type: application/json

{
  "error": "Too many requests",
  "message": "Rate limit exceeded. Please try again in 60 seconds",
  "retry_after": 60
}

相關標頭:
- Retry-After: 多久後可以重試(秒)
- X-RateLimit-Limit: 速率限制(每小時 100 次)
- X-RateLimit-Remaining: 剩餘次數
- X-RateLimit-Reset: 重置時間(Unix timestamp)

實作建議:
// 固定視窗(Fixed Window)
- 每小時允許 100 次請求
- 01:00-02:00 可以請求 100 次

// 滑動視窗(Sliding Window)
- 任意一小時內只能請求 100 次
- 更精確但複雜

// Token Bucket
- 令牌桶演算法
- 可以短時間突發流量

5️⃣ 5xx - 伺服器錯誤

500 Internal Server Error

說明:
- 伺服器內部錯誤
- 最常見的伺服器錯誤

使用場景:
- 程式碼錯誤(未處理的例外)
- 資料庫錯誤
- 第三方服務錯誤

範例:
【請求】
GET /api/users/123 HTTP/1.1

【回應】
HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
  "error": "Internal server error",
  "message": "An unexpected error occurred",
  "request_id": "abc-123-def"
}

實作建議:
// 不要暴露詳細錯誤給客戶端(安全考量)
❌ 不好:
{
  "error": "Database connection failed: password incorrect",
  "stack_trace": "..."
}

✅ 好:
{
  "error": "Internal server error",
  "message": "Please try again later",
  "request_id": "abc-123"
}

// 詳細錯誤記錄在伺服器日誌中

502 Bad Gateway

說明:
- 閘道錯誤
- 上游伺服器回應無效

使用場景:
- 反向代理無法連線到後端伺服器
- 後端伺服器回應格式錯誤

範例:
【架構】
客戶端 → Nginx(反向代理)→ 後端伺服器

【情況 1:後端伺服器掛了】
客戶端 → Nginx → 後端伺服器(無回應)

HTTP/1.1 502 Bad Gateway
Content-Type: text/html

<html>
<body>
  <h1>502 Bad Gateway</h1>
  <p>The server is temporarily unavailable</p>
</body>
</html>

【情況 2:後端伺服器回應格式錯誤】
Nginx 無法解析後端的回應

常見原因:
- 後端伺服器當機
- 後端伺服器過載
- 網路問題
- 部署中(伺服器重啟)

503 Service Unavailable

說明:
- 服務暫時無法使用
- 伺服器過載或維護中

使用場景:
- 伺服器維護
- 伺服器過載
- 暫時關閉服務

範例:
【回應】
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: application/json

{
  "error": "Service unavailable",
  "message": "Server is under maintenance",
  "retry_after": 3600,
  "maintenance_end": "2025-01-06T15:00:00Z"
}

502 vs 503:
502(Bad Gateway):
- 閘道問題
- 上游伺服器有問題
- 通常是意外發生

503(Service Unavailable):
- 服務暫時不可用
- 通常是計劃性的(維護)
- 或者伺服器主動拒絕(過載保護)

504 Gateway Timeout

說明:
- 閘道逾時
- 上游伺服器未在時限內回應

使用場景:
- 後端伺服器處理時間過長
- 網路延遲

範例:
【架構】
客戶端 → Nginx(30秒逾時)→ 後端伺服器

【情況】
後端伺服器處理了 35 秒才回應
Nginx 在 30 秒時放棄等待

【回應】
HTTP/1.1 504 Gateway Timeout
Content-Type: application/json

{
  "error": "Gateway timeout",
  "message": "The server took too long to respond"
}

解決方法:
1. 增加逾時設定
2. 優化後端效能
3. 使用非同步處理(長時間任務)

💻 實戰範例

Python Flask 實作各種狀態碼

from flask import Flask, jsonify, request

app = Flask(__name__)

# 200 OK
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    user = {"id": user_id, "name": "John"}
    return jsonify(user), 200

# 201 Created
@app.route('/api/users', methods=['POST'])
def create_user():
    data = request.get_json()
    new_user = {"id": 123, "name": data.get("name")}
    return jsonify(new_user), 201, {
        'Location': '/api/users/123'
    }

# 204 No Content
@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    # 刪除用戶...
    return '', 204

# 400 Bad Request
@app.route('/api/validate', methods=['POST'])
def validate():
    data = request.get_json()
    if not data or 'email' not in data:
        return jsonify({
            "error": "Bad request",
            "message": "Email is required"
        }), 400
    return jsonify({"status": "valid"}), 200

# 401 Unauthorized
@app.route('/api/profile', methods=['GET'])
def get_profile():
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({
            "error": "Unauthorized",
            "message": "Authentication required"
        }), 401
    return jsonify({"name": "John"}), 200

# 403 Forbidden
@app.route('/api/admin', methods=['GET'])
def admin():
    user_role = request.headers.get('X-User-Role')
    if user_role != 'admin':
        return jsonify({
            "error": "Forbidden",
            "message": "Admin access required"
        }), 403
    return jsonify({"data": "admin data"}), 200

# 404 Not Found
@app.route('/api/users/<int:user_id>', methods=['GET'])
def find_user(user_id):
    user = None  # 假設查詢資料庫
    if not user:
        return jsonify({
            "error": "Not found",
            "message": f"User {user_id} does not exist"
        }), 404
    return jsonify(user), 200

# 409 Conflict
@app.route('/api/users', methods=['POST'])
def register():
    data = request.get_json()
    username = data.get("username")

    # 檢查用戶名是否已存在
    if username_exists(username):
        return jsonify({
            "error": "Conflict",
            "message": f"Username '{username}' already exists"
        }), 409

    # 建立用戶...
    return jsonify({"id": 123}), 201

# 429 Too Many Requests
from functools import wraps
from time import time

request_counts = {}

def rate_limit(max_requests=10, window=60):
    def decorator(f):
        @wraps(f)
        def wrapped(*args, **kwargs):
            ip = request.remote_addr
            now = time()

            if ip not in request_counts:
                request_counts[ip] = []

            # 移除過期的請求記錄
            request_counts[ip] = [
                t for t in request_counts[ip]
                if now - t < window
            ]

            if len(request_counts[ip]) >= max_requests:
                return jsonify({
                    "error": "Too many requests",
                    "message": "Rate limit exceeded"
                }), 429, {
                    'Retry-After': str(window)
                }

            request_counts[ip].append(now)
            return f(*args, **kwargs)

        return wrapped
    return decorator

@app.route('/api/limited', methods=['GET'])
@rate_limit(max_requests=10, window=60)
def limited_endpoint():
    return jsonify({"data": "success"}), 200

# 500 Internal Server Error
@app.route('/api/error', methods=['GET'])
def error():
    try:
        # 模擬錯誤
        result = 1 / 0
    except Exception as e:
        # 記錄詳細錯誤到日誌
        app.logger.error(f"Error: {str(e)}")

        # 回傳簡化的錯誤訊息
        return jsonify({
            "error": "Internal server error",
            "message": "An unexpected error occurred",
            "request_id": "abc-123"
        }), 500

# 503 Service Unavailable
maintenance_mode = True

@app.before_request
def check_maintenance():
    if maintenance_mode and request.path != '/api/health':
        return jsonify({
            "error": "Service unavailable",
            "message": "Server is under maintenance"
        }), 503, {
            'Retry-After': '3600'
        }

if __name__ == '__main__':
    app.run(debug=True)

🎓 面試常見問題

Q1:401 和 403 有什麼差異?

A:401 是未認證,403 是已認證但無權限

401 Unauthorized(未認證):
- 沒登入或認證資訊無效
- 客戶端需要提供認證資訊
- 回應包含 WWW-Authenticate 標頭

場景:
- 沒有提供 Token
- Token 過期
- Token 格式錯誤
- 帳號密碼錯誤

範例:
GET /api/profile
(沒有 Authorization 標頭)

→ 401 Unauthorized
→ 提示:請登入

403 Forbidden(無權限):
- 已認證但權限不足
- 即使認證也無法存取
- 不包含 WWW-Authenticate

場景:
- 一般用戶嘗試存取管理員功能
- 用戶A 嘗試修改用戶B 的資料
- IP 被封鎖
- 帳號被停用

範例:
DELETE /api/users/456
Authorization: Bearer user_123_token
(user 123 嘗試刪除 user 456)

→ 403 Forbidden
→ 提示:權限不足

記憶技巧:
401:Who are you?(你是誰?)
403:I know who you are, but you can't do that(我知道你是誰,但你不能這樣做)

Q2:什麼時候用 200,什麼時候用 201?

A:根據是否建立新資源決定

200 OK:
- 請求成功
- 適用於 GET、PUT、PATCH、POST(不建立新資源時)

使用場景:
- GET:取得資源
- PUT:更新現有資源
- PATCH:部分更新
- POST:執行操作(不建立資源)

範例:
GET /api/users/123     → 200 OK
PUT /api/users/123     → 200 OK(更新現有用戶)
POST /api/login        → 200 OK(登入操作,不建立資源)

201 Created:
- 成功建立新資源
- 適用於 POST(建立新資源時)
- 應該包含 Location 標頭

使用場景:
- POST:建立新資源

範例:
POST /api/users
{"name": "John"}

→ 201 Created
→ Location: /api/users/123
→ Body: {"id": 123, "name": "John"}

特殊情況:
PUT 也可以回傳 201(當資源不存在時建立)

PUT /api/users/999
{"name": "Alice"}

如果用戶 999 不存在:
→ 201 Created(建立新資源)

如果用戶 999 已存在:
→ 200 OK(更新現有資源)

Q3:什麼時候用 204,什麼時候用 200?

A:根據是否需要回傳內容決定

200 OK(有回傳內容):
- 客戶端需要知道結果

範例:
PUT /api/users/123
{"name": "John Updated"}

→ 200 OK
→ Body: {"id": 123, "name": "John Updated", "updated_at": "..."}

POST /api/login
{"username": "john", "password": "123"}

→ 200 OK
→ Body: {"token": "...", "user": {...}}

204 No Content(無回傳內容):
- 操作成功但不需要回傳資料
- 節省頻寬

範例:
DELETE /api/users/123

→ 204 No Content
→ 沒有 Body

PUT /api/users/123
{"name": "John Updated"}

→ 204 No Content
→ 沒有 Body(客戶端不需要更新後的資料)

選擇建議:
1. DELETE:通常用 204(刪除後沒有內容可回傳)
2. PUT/PATCH:
   - 客戶端需要更新後的資料 → 200
   - 客戶端不需要資料 → 204
3. POST:
   - 建立資源 → 201 + 內容
   - 執行操作 → 200 + 結果 或 204

實務考量:
- RESTful API:通常回傳內容(200/201)
- 效能優先:可以用 204 減少資料傳輸
- 客戶端需求:如果客戶端需要確認結果,回傳內容

Q4:302 和 301 有什麼差異?

A:暫時 vs 永久重新導向

301 Moved Permanently(永久重新導向):
- 資源永久移到新位置
- 搜尋引擎更新索引
- 瀏覽器可能快取

使用場景:
- 網站改版(URL 結構改變)
- HTTP 強制跳轉到 HTTPS
- 舊域名重新導向到新域名

範例:
GET /old-page HTTP/1.1

→ 301 Moved Permanently
→ Location: /new-page

→ 搜尋引擎:移除 /old-page,收錄 /new-page
→ 瀏覽器:下次直接訪問 /new-page

302 Found(暫時重新導向):
- 資源暫時在另一個位置
- 搜尋引擎不更新索引
- 瀏覽器不快取

使用場景:
- 臨時維護頁面
- A/B 測試
- 根據條件重新導向

範例:
GET /promotion HTTP/1.1

→ 302 Found
→ Location: /special-offer

→ 搜尋引擎:保留 /promotion
→ 瀏覽器:下次還是先訪問 /promotion

對 SEO 的影響:
301:
- 搜尋引擎權重轉移到新 URL
- 適合永久性改變

302:
- 搜尋引擎保留原 URL
- 適合臨時性重新導向

實務建議:
- 不確定時用 302(比較安全)
- 確定永久改變才用 301
- 301 一旦生效,很難撤銷(瀏覽器快取)

Q5:如何選擇正確的狀態碼?

A:根據操作結果和資源狀態選擇

決策流程:

1. 請求成功了嗎?
   ├─ 是 → 2xx
   └─ 否 → 繼續

2. 是誰的錯?
   ├─ 客戶端 → 4xx
   └─ 伺服器 → 5xx

3. 是否需要重新導向?
   └─ 是 → 3xx

詳細決策:

【成功(2xx)】
- 成功取得資源 → 200 OK
- 成功建立資源 → 201 Created
- 成功但無內容 → 204 No Content
- 部分內容 → 206 Partial Content

【重新導向(3xx)】
- 永久重新導向 → 301 Moved Permanently
- 暫時重新導向 → 302 Found
- 資源未修改(使用快取)→ 304 Not Modified

【客戶端錯誤(4xx)】
- 請求格式錯誤 → 400 Bad Request
- 未登入 / 認證無效 → 401 Unauthorized
- 權限不足 → 403 Forbidden
- 資源不存在 → 404 Not Found
- HTTP 方法不允許 → 405 Method Not Allowed
- 資源衝突 → 409 Conflict
- 請求過於頻繁 → 429 Too Many Requests

【伺服器錯誤(5xx)】
- 伺服器內部錯誤 → 500 Internal Server Error
- 閘道錯誤 → 502 Bad Gateway
- 服務暫時無法使用 → 503 Service Unavailable
- 閘道逾時 → 504 Gateway Timeout

實際範例:

場景 1:用戶登入
- 成功 → 200 OK
- 帳號密碼錯誤 → 401 Unauthorized
- 帳號被停用 → 403 Forbidden
- 伺服器錯誤 → 500 Internal Server Error

場景 2:建立文章
- 成功 → 201 Created
- 缺少標題 → 400 Bad Request
- 未登入 → 401 Unauthorized
- 標題重複 → 409 Conflict

場景 3:刪除文章
- 成功 → 204 No Content
- 文章不存在 → 404 Not Found
- 不是作者 → 403 Forbidden
- 文章已刪除 → 404 Not Found 或 410 Gone

常見錯誤:
❌ 所有錯誤都回傳 200
   → 應該使用適當的 4xx/5xx

❌ 資源不存在回傳 500
   → 應該回傳 404

❌ 權限不足回傳 401
   → 應該回傳 403(已認證但無權限)

✅ 重點回顧

五大類狀態碼:

  • 1xx - 資訊性(100 Continue, 101 Switching Protocols)
  • 2xx - 成功(200 OK, 201 Created, 204 No Content)
  • 3xx - 重新導向(301, 302, 304)
  • 4xx - 客戶端錯誤(400, 401, 403, 404, 429)
  • 5xx - 伺服器錯誤(500, 502, 503, 504)

最常用的狀態碼:

  • 200 OK - 請求成功
  • 201 Created - 資源建立成功
  • 204 No Content - 成功但無內容
  • 400 Bad Request - 請求格式錯誤
  • 401 Unauthorized - 未認證
  • 403 Forbidden - 無權限
  • 404 Not Found - 找不到資源
  • 500 Internal Server Error - 伺服器錯誤

重要差異:

  • 401 vs 403 - 未認證 vs 無權限
  • 200 vs 201 - 一般成功 vs 建立資源
  • 200 vs 204 - 有內容 vs 無內容
  • 301 vs 302 - 永久 vs 暫時重新導向

面試重點:

  • ✅ 能區分 401 和 403
  • ✅ 知道何時用 201(建立資源)
  • ✅ 理解 301 和 302 的差異
  • ✅ 能根據情況選擇正確狀態碼
  • ✅ 了解 5xx 通常是伺服器問題

上一篇: 02-3. HTTP 方法(GET、POST、PUT、DELETE) 下一篇: 02-5. HTTP Headers 詳解


最後更新:2025-01-06

0%