淺談HTTP之URL

ma125120發表於2019-01-17

背景介紹

從畢業到工作都一年多了,在這一年多的時間裡,我發現有的後臺程式設計師連 URL 的查詢引數都不知道,再加上最近使用 node 和 python 連線加密的 mongodb 時也遇到了點問題,就決定對我所知道的 URL 方面知識做一個總結。

何為URL

名詞解釋

URL 是 Uniform Resource Location 的縮寫,譯為“統一資源定位符”。通俗地說, URL 是 Internet 上用來描述資訊資源的字串,主要用在各種 www 客戶程式和伺服器程式上。採用URL可以用一種統一的格式來描述各種資訊資源,包括檔案、伺服器的地址和目錄等。

順便也提一下 URI 吧:

Web上可用的每種資源 - HTML文件、影象、視訊片段、程式等 - 由一個通過通 用資源標誌符(Universal Resource Identifier, 簡稱"URI")進行定位。

URI一般由三部分組成:

  • 訪問資源的命名機制。
  • 存放資源的主機名。
  • 資源自身的名稱,由路徑表示。

URL 是 URI 的子集,但是平時的開發中我們只需要瞭解 URL 就可以了。

URL格式

http://test.com:8080/example/index.html為例進行說明。

URL的格式由下列三部分組成:

  • 第一部分是協議(或稱為服務方式),本例中為HTTP 協議
  • 第二部分是存有該資源的主機IP地址或域名(有時也包括埠號),本例中為 test.com:8080。平時看到的都是域名,之後客戶端會通過DNS(域名系統) 查詢域名對應的 IP,然後根據IP和埠號進行伺服器的連線。稍後會對此進行詳細說明。
  • 第三部分是主機資源的具體地址,如目錄和檔名等,本例中為 /example/index.html

URL 的語法

通用語法

<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

  • scheme: 協議,常見的有 http(80),https(443),mailto,ftp(21),rtsp,rtspu,file。
  • user:使用者名稱。
  • password: 密碼。
  • host:主機。
  • port: 埠。
  • params: 引數。通常為 key=value
  • query:查詢引數或查詢字串。
  • frag: 片段(在瀏覽器中會被解析為 window.location.hash)。

這只是通用語法,大多數的 URL 都只遵循了一部分而已,並不是每種URL都會有上面的所有資訊。

從上面的語法我們可以看出至少 :/@;?# 都是敏感字元,所以在其他引數中不能包含這些字元,如果含有敏感字元或者特殊字元,就需要使用對應的轉義字元,否則可能會發生不可預料的結果。 在開發過程中,容易在查詢引數中含有 敏感字元,所以查詢字串的值一般都需要使用 encodeURIComponent進行編碼。

http(s)中的語法

http://test.com:8080/user/index.html?id=1&nickName=test#/list

在瀏覽器中被解析後的格式為:

{
    protocol: 'http:', // 協議
    host: 'test.com:8080', // 主機名或域名,帶埠號
    hostname: 'test.com', // 主機名或域名,不帶埠號
    port: '', // 埠號,http預設80,https預設443
    path: '/user/index.html', // 路徑
    query: '?id=1&nickName=test'// 查詢字串
    hash: '#list', //片段或者雜湊,
}
複製程式碼

注意: #後面的東西都不會從客戶端傳到伺服器。

在位址列中改變 # 後的字串時,頁面並不會重新整理,但是會出發 hashchange 事件,很多前端路由的 hash 模式就是根據這種特性實現的。而改變出了frag(hash)之外的東西都會導致瀏覽器重新整理,因為此時相當於向伺服器發出了一個新的請求。

ftp中的語法

檔案傳輸協議,可以用來從伺服器下載或上傳檔案。

基本格式:

ftp://<user>:<password>@<host>:<port>/<path>;<params>

示例:

ftp://ftpuser:123456.com@test.com:21/path/example

file中的語法

該協議常見於本地檔案於瀏覽器中開啟的場景,也可能是網路檔案系統或者其他一些檔案共享系統。這個我就不細說了,感覺沒啥可說的。

mongodb協議

常見於後臺程式連線 mongodb 資料庫。雖然我們常見的包都是用物件格式來連線資料庫的,但是最後都會被轉化為字串形式的。

基本格式:

mongodb://<user>:<password>@<host>:<port>/<path>?<query>

示例:

mongodb://test:123456@127.0.0.1:27017/novel?authSource=admin 該字串表示以 mongodb 協議,使用者名稱為test,密碼為123456,資料庫dbName為novel,驗證的資料庫為admin,連線至127.0.0.1主機的27017埠。

注意:如mongodb伺服器沒有開啟加密,需要去掉查詢引數,否則會連線失敗。若開啟了加密,authSource的值必須和前方的user和password相對應。,否則使用者驗證頁無法連線。 我用的thinkjs,一開始沒有配置 authSource,給我報的錯誤是連線超時,當時還納悶遠端的伺服器再慢也不至於吧???後來調整了超時時間,發現還是超時,而且一直在嘗試重新連線,就想到了可能是驗證失敗了,這個糾結了不少時間。

在瀏覽器中輸入URL後,執行的全部過程

整個流程如下:

  1. 域名解析;
  2. 發起TCP的3次握手;
  3. 建立TCP連線後發起http請求;
  4. 伺服器響應htp請求;
  5. 瀏覽器解析htm程式碼,並請求html程式碼中的資源(如js、css、圖片等);
  6. 斷開TCP連線;
  7. 瀏覽器對頁面進行渲染呈現給使用者。

其實,域名解析這個過程要是細說的話還是有點複雜的,總之有時候也是蠻耗時的,畢竟從 解析 這個用詞我們就能看出它一定是需要時間的?通過DNS會將域名解析為 IP,之後會根據 IP 和 埠連線伺服器。

如果我們直接用IP訪問伺服器,就可以節省一部分時間,但還是不建議大家這麼做,因為如果更換了伺服器的話,域名可以解析到另一個IP,可以在瀏覽器端保留相應的資料,而IP就不行了。還有就是,域名的可訪問性和可讀性可比IP強多了。

動態伺服器和靜態伺服器

上面的大部分都只適用於靜態伺服器,如果是動態伺服器的話,它會有自己的一套解析規則,但是大致上也是相同的,最大的區別可能還是動態伺服器的動態路由吧(前端路由現在也支援動態路由了)。

下面主要就說一下動態伺服器相對於靜態伺服器的特殊點:

動態路由

比如定義一個獲取某個人的資訊的介面,它的路徑為:{ path: '/user/:id' }

當訪問 /user/1?name=test時,該介面將被框架解析為:

{
    params: {
        id: 1,
    },
    query: {
        name: 'test',
    }
}
複製程式碼

如果是靜態路由的話,只能使用 /user?id=1&name=test這種方式來傳遞引數了,從這可以看出動態路由在一定程度上還是比靜態路由有一點優勢的;而且動態路由看上去更優雅。

但是如果後臺對路由的定義不好,前端傳過去的引數為空的話,動態路由就會變成 /user/,訪問到的介面就不是 /user/:id這個路由,而是 /user/這個介面了,後臺差找不到該路由,直接就返回 404 或者自定義的錯誤了。

路由重寫

我接觸過的後臺框架有 ThinkPHP 和 thinkjs, 這兩個還是比較相似的,它們都有一個特性就是能夠進行路由重寫,比如在定義好的路由之後加上字尾,如果設定 ext: '.html',那麼訪問 /user/1.html時將被解析為 /user/:id,在那些前後端還未分離的公司,這個應該是主要的頁面輸出方式了(記得不知道從哪看到的這樣好像有利於SEO優化?)。

所以以後在看到 http://test.com/user/1.html 這樣的 URL的時候,就不能再單純的認為它一定指向伺服器的某個靜態檔案,它現在也可能是經過模板渲染之後的一個響應。

說了點動態伺服器的,那就再說一點靜態伺服器的吧,因為我遇到的一個後臺,在前端上傳檔案後,直接把檔案儲存到linux系統的根目錄,然後在域名後面把檔案的路徑拼上去,還納悶怎麼就是訪問不到呢?

甘拜下風

web伺服器的根目錄

常用的靜態伺服器是 nginx,設定一下靜態檔案的壓縮啦,快取啦,代理啦,識別裝置進行跳轉,圖片裁剪之類的,都是so easy,簡直就是我們大前端的標配嘛。

淺談HTTP之URL
話雖然這麼說,現在的公司還是把我的前端檔案放在tomcat容器下,並沒有給我一個專屬的 nginx 伺服器,o(╥﹏╥)o。每次改完檔案我得先打包成壓縮包,發給後臺,他放到 tomcat 容器下,然後再上傳至伺服器,重啟容器,最後快取沒控制好,壓縮也沒搞。重啟之後還得懷疑一下我的檔案是不是發錯了,這檔案怎麼沒更新???其實用 nginx,再搭配 git 的 hooks,幾個命令列的事而已嘛,扯遠了,還是說說 web伺服器的根目錄吧。

流行一點的web伺服器主要是 nginx, apache,tomcat,後兩個主要還是用於搭配後臺使用,不直接向外暴露介面的。這些靜態伺服器都會有一個配置用於設定 web 伺服器的根目錄,那麼這個根目錄的作用是什麼呢?就是控制客戶端能訪問到的頂級目錄。 比如根目錄是 www,是不能訪問 www 目錄以外的其他檔案的,只能訪問 www 的子目錄的各檔案。

參考連結

相關文章