真正「搞」懂HTTP協定08之重定向

2023-01-17 06:00:54

  我們知道,用來傳輸頁面的協定就是HTTP協定,全稱是超文字傳輸協定,而瀏覽器展示的頁面則是用HTML編寫的,HTML的全稱則是超檔案標示語言。你看,都叫做超文字,我在第一篇文章的時候也詳細的聊過,超文字區別於文字的本質就是文字中具有超連結的文字。

  當我們點選頁面中的超連結,則會跳轉到其它超文字頁面,對於線性傳統結構的檔案,是一個根本性的變革。

一、重定向的基本概念和場景

  點選超連結後,瀏覽器會解析URL,再用這個URL發起一個新的HTTP請求,跳轉到其它頁面。這樣的跳轉動作,是由瀏覽器的使用者發起的,這種跳轉叫做「主動跳轉」,但是還有一類跳轉,是由伺服器發起的,瀏覽器使用者無法控制,也就是「被動跳轉」,被動跳轉在HTTP中有一個專有的名詞,叫做重定向

  重定向的使用場景其實並不少,比如,多個域名指向一個統一的頁面。我們可以在瀏覽器裡開啟一個新的tab標籤,然後輸入http://www.baidu.com,看一下,什麼效果?

   我們發現會優先有兩個請求,一個307,一個200。我們來看下詳細的內容:

 

   我們看下,其實並不複雜,就是重定向到https協定的域名。

  嗯……這就是一個比較常見的重定向場景之一。再比如一些資源的更新,舊的不用了,但是為了不去修改HTML資源存取的程式碼,緊急解決一些問題,就可以由伺服器更改,重定向到新的資源頁面。

  詳細的內容,我們在後續的講解和例子中再深入的學習。

二、重定向涉及到的頭欄位和狀態碼

  我們要聊的欄位其實不多,就一個,就是Location欄位,Location欄位可以接受絕對地址或者相對地址作為該欄位的值,如果是相對地址的話,則會從響應報文的上下文中獲取相應的資訊,拼出完整的重定向地址。如果重定向只是在站內跳轉,比如www.zaking.com跳轉到www.zaking.com/index.html,那你可以隨便用,但是如果你要跳到別的站點,比如從www.zaking.com跳轉到www.baidu.com,則必須使用完整的絕對地址。

  涉及到重定向的狀態碼則比較多了,我們要能準確地區分出不同的3xx狀態碼代表的含義。最常見的狀態碼就是301、302,還有比如303,304,305,306,307我們稍微瞭解下,注意其語意的區別即可。

  首先,301狀態碼的意思是Moved Permanently,也就是永久重定向,意味著你存取的資源已經不存在了,今後所有的請求都要使用新的URI。當瀏覽器看到301狀態碼時,就知道原來的URI已經過時了,會適當的做一些優化。比如歷史記錄,更新書籤,下次存取的時候,就不會再往舊地址傳送請求了。而搜尋引擎看到301,也會更新索引庫,不會在去爬原來的地址了。

  當然,這裡你要知道的就是,這些一切的優化也好,更新也罷,其實都是終端針對協定所做的應用層面的操作。比如,瀏覽器會根據301來進行優化,搜尋引擎也會根據301來實現更新策略,那瀏覽器不實現行不行?搜尋引擎也不做這些額外的無聊的操作,肯定都是可以的~~

  接下來我們說說302,是指臨時重定向,也就是說你存取的地址暫時不能用了,先去新的地址存取資源吧。但由於是臨時重定向,瀏覽器也好,搜尋引擎也好,還是其它的啥啥也好,都不會做什麼優化和更新,只是做個重定向的操作就完事了。

  那麼我們再來看看其它的不常用的狀態碼:

  1. 303,沒啥用~意思是See Other,參見其它的意思,但要求重定向後的請求改為 GET 方法,存取一個結果頁面,避免 POST/PUT 重複操作;。其實302做的事情,跟303一樣,用302就行了。
  2. 304,未修改,Not Modified,如果使用者端執行了一個有條件的Get請求,但是請求的資源並沒有修改,則會返回304。有條件的Get請求,其實就是指那些帶有If-開頭的頭欄位,需要根據這些欄位進行一些其它的邏輯處理。瞭解下就行啦。
  3. 305,Use Proxy,所提供的Location欄位的值必須是一個代理伺服器的地址。
  4. 306,廢棄了。不用管
  5. 307,Temporary Redirect,類似 302,但重定向後請求裡的方法和實體不允許變動,含義比 302 更明確。

  所以你看,除了301和302以外,其它大多數的欄位要麼場景分的更細,實踐的時候大多數都由302處理了,要麼就廢棄了。我們熟悉301和302就足夠了。

三、重定向的應用場景

  我們之前在聊狀態碼的時候,強調了301和302的重要性,換句話就是說,其實重定向可以粗略、簡單、明瞭的理解為永久和臨時的區別。那麼針對重定向的使用場景,實際上也是基於永久和臨時的區別和特點來實踐的。

  那什麼時候需要重定向呢?一個最常見的原因就是「資源不可用」,我們需要提供一個新資源的URI來進行後續的使用。至於不可用的原因那就種類繁多千變萬化了,比如伺服器維護,為了使用者使用不受到影響,則會重定向到一個臨時的頁面,供使用者存取。

  另一個原因就是增加存取入口,讓多個名字類似的域名指定到同一個主站,增加存取的入口同時還不會增加什麼工作量。

  在確定了重定向的場景後,要考慮的就是臨時還是永久了。那麼針對我們上面提到的兩種場景,要用臨時還是永久呢?

四、例子

  我們聊完了重定向的核心概念,接下來我們就來寫一寫例子,在實際的程式碼實驗中,體驗下301和302的區別。按照慣例,基本的程式碼我就不貼在這裡了,我只貼核心的部分了噢。首先,我們先來看看永久重定向:

if (parsedUrl.pathname == "/301") {
  let sourceCode = fs.readFileSync(
    path.resolve(__dirname, "./index.html"),
    "utf8"
  );
  res.writeHead(301, {
    Location: "http://www.zaking.com:9001/redirect",
  });
  res.end(sourceCode);
}

  其實就真的很簡單,我感覺沒啥說的。302的也一樣,把上面的頭欄位改成302就好了。我感覺這例子不用寫,結果我就不展示了,大家有興趣可以自己試下,我們來看點不一樣的:

   看到區別麼?301永久重定向,如果你不做快取的設定,那麼瀏覽器會預設快取原地址,因為瀏覽器認為你的重定向是永久的,我直接快取就好了,而302臨時重定向的話,瀏覽器壓根不會快取,因為覺得這個地址以後還要用,快取也沒用。我們再來看個例子:

// 死迴圈了
if (parsedUrl.pathname == "/cycle") {
  let sourceCode = fs.readFileSync(
    path.resolve(__dirname, "./cycle.html"),
    "utf8"
  );
  res.writeHead(302, {
    Location: "http://www.zaking.com:9001/back",
  });
  res.end(sourceCode);
}
if (parsedUrl.pathname == "/back") {
  let sourceCode = fs.readFileSync(
    path.resolve(__dirname, "./back.html"),
    "utf8"
  );
  res.writeHead(302, {
    Location: "http://www.zaking.com:9001/cycle",
  });
  res.end(sourceCode);
}

  程式碼沒啥哈,重點在於迴圈跳轉,直接造成瀏覽器壓根沒法用:

   這是重定向需要尤其注意的問題,咱們現在的重定向鏈路十分簡單,就兩個頁面來回跳,當你寫了複雜的伺服器邏輯的時候,很難確定是否會有迴圈鏈路的重定向的問題的。

五、總結

  其實關於重定向,最核心的就是301和302了,大家一定要會,沒啥好說的。重定向在一定程度上提供了一定場景下的應用解決方案,但是其實也會帶來一定的問題,比如,只要重定向就一定會傳送一次額外的請求,造成效能的浪費。另外要格外注意的就是迴圈跳轉的問題。

  本篇的東西不多,那就到這裡啦,學習這麼無聊,下一篇我們來吃點點心。