本篇文章將針對 HTTP 協定進行深入探討,並設計實作實驗(lab)來幫助讀者更好地理解這個重要的網路協定。
文章將涵蓋以下三個關鍵主題:
想像一下,HTTP 請求方法就像是你在餐廳點餐時,對服務生說的不同指令。
每種方法都有特定的用途,就像你點不同的菜一樣。以下是常見 HTTP 方法的簡單解釋:
// routes/httpHandlers.js
// 處理所有 HTTP 方法的路由
app.all('/method', (req, res) => {
  const method = req.method;
  res.send(`The HTTP method used was: ${method}`);
});
const { handleMethod } = require('./routes/httpHandlers');
// 處理所有 HTTP 方法的路由
app.all('/method', handleMethod);
app.all() 方法如何處理所有類型的 HTTP 請求。
curl -X [HTTP_METHOD] [URL]
其中,[HTTP_METHOD] 是你想要使用的 HTTP 方法,[URL] 是你要請求的網址。
curl http://nodelab.feifei.tw/method/api/resource
GET 是預設的方法,所以不需要 -X 參數。
curl -X POST -d "param1=value1¶m2=value2" http://nodelab.feifei.tw/method/api/resource
-d 參數用來發送 POST 資料。
curl -X PUT -d "param1=value1¶m2=value2" http://nodelab.feifei.tw/method/api/resource
curl -X DELETE http://nodelab.feifei.tw/method/api/resource
curl -X PATCH -d "param1=value1" http://nodelab.feifei.tw/method/api/resource
curl -I http://nodelab.feifei.tw/method
-I 參數相當於發送 HEAD 請求。
curl -X OPTIONS http://nodelab.feifei.tw/method
HTTP 狀態碼是伺服器對客戶端請求的回應,用於表示請求處理的結果。了解這些狀態碼有助於我們判斷請求的處理情況,並找出潛在的問題。
HTTP 狀態碼分為五類,每類都有其特定的意義:
在開發 Web 應用時,正確使用 HTTP 狀態碼可以:
 
// routes/httpHandlers.js
app.all('/status/:code', (req, res) => {
  const code = parseInt(req.params.code);
  const message = getStatusMessage(code);
  res.status(code).json({ code, message });
});
// 輔助函數:取得狀態碼對應的訊息
function getStatusMessage(code) {
  const messages = {
      // 1xx 訊息
      100: '繼續處理中',
      101: '切換協定',
      102: '處理中', // RFC 2518 WebDAV 擴充
      // 2xx 成功
      200: '請求成功',
      201: '資源已建立',
      202: '已接受處理,但尚未完成',
      204: '無內容,無需回應',
      206: '部分內容', // 針對範圍請求
      // 3xx 重定向
      300: '多種選擇',
      301: '資源永久移動',
      302: '資源暫時移動',
      303: '請查看其他位置',
      304: '資源未修改',
      307: '暫時重定向',
      308: '永久重定向',
      // 4xx 客戶端錯誤
      400: '錯誤的請求',
      401: '未授權,請登入',
      403: '禁止存取',
      404: '找不到資源',
      405: '不允許的請求方法',
      406: '不接受的回應格式',
      408: '請求超時',
      409: '資源衝突',
      410: '資源已永久移除',
      429: '請求次數過多,請稍後再試',
      // 5xx 伺服器錯誤
      500: '伺服器內部錯誤',
      501: '功能尚未實作',
      502: '錯誤的網關',
      503: '服務目前無法使用',
      504: '網關超時',
      505: '不支援的 HTTP 版本'
      
  };
  return messages[code] || '未知的狀態碼';
}
const { handleMethod, handleStatus } = require('./routes/httpHandlers');
// 處理所有 HTTP 方法的路由
app.all('/method', handleMethod);
// 狀態碼處理路由
app.all('/status/:code', handleStatus);

curl -i http://nodelab.feifei.tw/status/404


HTTP 標頭是請求和回應中不可或缺的一部分,它們攜帶了關於傳輸、安全性、內容類型等重要資訊。然而,某些標頭可能會洩露敏感資訊,如伺服器版本,這些資訊可能被惡意使用者利用來進行攻擊。
在身分驗證的環境中,HTTP 標頭再次扮演了關鍵角色,特別是在基本認證(Basic Authentication)的實現中。
身分驗證是網站安全的核心環節之一,用於驗證使用者的身分,並根據其權限授予對應的資源訪問權。
每種方法都有其優缺點和適用場景。在實際應用中,常常會結合多種方法來增強安全性並改善使用者體驗。選擇合適的驗證方式需要考慮安全需求、使用者友善度、技術實作難度等多個因素。
身分驗證是確保系統安全的重要環節。在 Web 應用程式中,基本認證是一種簡單但廣泛使用的方法。
// routes/authHandler.js
// 模擬的使用者資料庫,實際應用中應該從資料庫查詢
const users = {
    'admin': 'password123',
    'user': 'userpass'
};
function authenticateBasic(req, res, next) {
    const authHeader = req.headers.authorization;
    
    if (!authHeader) {
        res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
        return res.status(401).send('認證失敗:需要提供認證資訊。');
    }
    const auth = Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
    const user = auth[0];
    const pass = auth[1];
    if (users[user] && users[user] === pass) {
        next();
    } else {
        res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
        return res.status(401).send('認證失敗:使用者名稱或密碼錯誤。');
    }
}
// 受保護的路由處理器
function protectedRoute(req, res) {
    res.send('歡迎來到受保護的區域!');
}
module.exports = {
    authenticateBasic,
    protectedRoute
};
模擬使用者資料庫:
const users = {
    'admin': 'password123',
    'user': 'userpass'
};
這是一個簡單的物件,用於模擬使用者資料庫。在實際應用中,這應該被替換為真實的資料庫查詢。
authenticateBasic 函數:
這是一個 Middleware 函數,用於處理基本認證。
檢查授權標頭:
const authHeader = req.headers.authorization;
從請求標頭中取得 Authorization 欄位。
處理未提供認證資訊的情況:
if (!authHeader) {
    res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
    return res.status(401).send('認證失敗:需要提供認證資訊。');
}
如果沒有提供認證資訊,設定 WWW-Authenticate 標頭並返回 401 狀態碼。
解碼和驗證認證資訊:
const auth = Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
const user = auth[0];
const pass = auth[1];
解碼 base64 編碼的認證資訊,並分離使用者名稱和密碼。
驗證使用者:
if (users[user] && users[user] === pass) {
    next();
} else {
    res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
    return res.status(401).send('認證失敗:使用者名稱或密碼錯誤。');
}
檢查使用者名稱和密碼是否相同。如果相同,調用 next() 繼續處理請求;否則,返回 401 錯誤。
const { authenticateBasic, protectedRoute } = require('./routes/authHandler');
// 設定受保護的路由
app.get('/protected', authenticateBasic, protectedRoute);
這段程式碼將 authenticateBasic Middleware 應用到 /protected 路由,確保只有通過認證的使用者才能訪問。
http://nodelab.feifei.tw/protected 
 
curl -v http://nodelab.feifei.tw/protected
 
curl -H 指定標頭curl -H "Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM=" -v http://nodelab.feifei.tw/protected 
YWRtaW46cGFzc3dvcmQxMjM=
Base64 是一種用來將二進位資料轉換成文字的編碼方式,主要使用 64 個可列印的字元來表示資料,這使得資料可以安全地在需要純文字傳輸的環境中傳送(例如在 URL、電子郵件等情況下)。
Base64 是一種將數位資料編碼成文字的技術。你可以把它想像成一個翻譯器,它把電腦使用的 0 和 1(也就是位元資料)轉換成我們能夠閱讀的字母和符號。
Base64 編碼的基本原理是:
當需要編碼的資料長度不是 3 的倍數時,Base64 會在結果中加上等號來表示這個資料的結尾。這樣可以確保編碼結果是完整且準確的。
這樣的方式可以幫助資料在不同系統之間傳輸時保持一致性,並且避免因為某些系統只接受文字格式而產生的問題。

 
HTTP 請求方法中,哪一種方法適合用來提交敏感資料?
答案: B) POST
哪一個狀態碼代表請求成功?
答案: C) 200
在基本驗證中,使用者的帳號和密碼是如何傳送的?
答案: B) Base64 編碼
HTTP 請求方法中,哪一種方法用於查詢伺服器支援的 HTTP 方法?
答案: A) OPTIONS
以下哪一項是 Base64 編碼的特徵?
答案: C) 最後可能出現一個或兩個等號
httpHandlers.js,處理所有 HTTP 方法。app.all('/method', handleMethod) 接收請求並回應使用的請求方法。curl 測試請求
curl http://nodelab.feifei.tw/method
curl -X POST -d "param1=value1¶m2=value2" http://nodelab.feifei.tw/method
curl -X PUT -d "param1=value1¶m2=value2" http://nodelab.feifei.tw/method
curl -X DELETE http://nodelab.feifei.tw/method
curl -X PATCH -d "param1=value1" http://nodelab.feifei.tw/method
curl -I http://nodelab.feifei.tw/method
curl -X OPTIONS http://nodelab.feifei.tw/method
httpHandlers.js 以處理狀態碼請求。curl -i http://nodelab.feifei.tw/status/404 測試。authHandler.js 中實作基本驗證。app.get('/protected', authenticateBasic, protectedRoute);