[譯]什么是 web 框架?


本文系轉載于網絡。

Web 應用框架,或者簡單的說是“Web 框架”,其實是建立 web 應用的一種方式。從簡單的博客系統到復雜的富 AJAX 應用,web 上每個頁面都是通過寫代碼來生成的。我發現很多人都熱衷于學習 web 框架技術,例如 Flask 或這 Django 之類的,但是很多人并不理解什么是 web 框架,或者它們是如何工作的。這篇文章中,我將探索反復被忽略的 web 框架基礎的話題。閱讀完這篇文章,你應該首先對什么是 web 框架以及它們為什么會存在有更深的認識。這會讓你學習一個新的 web 框架變得簡單的多,還會讓你在使用不同的框架的時候做個明知的選擇。

Web 如何工作的?

在我們討論框架之前,我們需要理解 Web 如何“工作”的。為此,我們將深入挖掘你在瀏覽器里輸入一個 URL 按下 Enter 之后都發生了什么。在你的瀏覽器中打開一個新的標簽,瀏覽http://www.jeffknupp.com我們討論為了顯示這個頁面,瀏覽器都做了什么事情(不關心 DNS 查詢)。

Web 服務器

每個頁面都以 HTML 的形式傳送到你的瀏覽器中,HTML 是一種瀏覽器用來描述頁面內容和結構的語言。那些負責發送 HTML 到瀏覽器的應用稱之為“Web 服務器”,會讓你迷惑的是,這些應用運行的機器通常也叫做 web 服務器。

然而,最重要的是要理解,到最后所有的 web 應用要做的事情就是發送 HTML 到瀏覽器。不管應用的邏輯多么復雜,最終的結果總是將 HTML 發送到瀏覽器(我故意將應用可以響應像 JSON或者 CSS等不同類型的數據忽略掉,因為在概念上是相同的)。

web 應用如何知道發送什么到瀏覽器呢?它發送瀏覽器請求的任何東西。

HTTP

瀏覽器從 web 服務器(或者叫應用服務器)上使用 HTTP 協議下載網站,HTTP 協議是基于一種 請求-響應(request-response)模型的。客戶端(你的瀏覽器)從運行在物理機器上的 web 應用請求數據,web 應用反過來對你的瀏覽器請求進行響應。

重要的一點是,要記住通信總是由客戶端(你的瀏覽器)發起的,服務器(也就是 web 服務器)沒有辦法創建一個鏈接,發送沒有經過請求的數據給你的瀏覽器。如果你從 web 服務器上接收到數據,一定是因為你的瀏覽器顯示地發送了請求。

HTTP Methods

在 HTTP 協議中,每條報文都關聯方法(method 或者 verb),不同的 HTTP 方法對應客戶端可以發送的邏輯上不同類型的請求,反過來也代表了客戶端的不同意圖。例如,請求一個 web 頁面的 HTML,與提交一個表單在邏輯上是不同的,所以這兩種行為就需要使用不同的方法。

HTTP GET

GET 方法就像其聽起來的那樣,從 web 服務器上 get(請求)數據。GET 請求是到目前位置最常見的一種 HTTP 請求,在一次 GET 請求過程中,web 應用對請求頁面的 HTML 進行響應之外,就不需要做任何事情了。特別的,web 應用在 GET 請求的結果中,不應該改變應用的狀態(比如,不能基于 GET 請求創建一個新帳號)。正是因為這個原因,GET 請求通常認為是“安全”的,因為他們不會導致應用的改變。

HTTP POST

顯然,除了簡單的查看頁面之外,應該還有更多與網站進行交互的操作。我們也能夠向應用發送數據,例如通過表單。為了達到這樣的目的,就需要一種不同類型的請求方法:POST。POST 請求通常攜帶由用戶輸入的數據,web 應用收到之后會產生一些行為。通過在表單里輸入你的信息登錄一個網站,就是 POST 表單的數據給 web 應用的。

不同于 GET 請求,POST 請求通常會導致應用狀態的改變。在我們的例子中,當表單 POST 之后,一個新的賬戶被創建。不同于 GET 請求,POST 請求不總是生成一個新的 HTML 頁面發送到客戶端,而是客戶端使用響應的響應碼(response code

)來決定對應用的操作是否成功。

HTTP Response Codes

通常來說,web 服務器返回 200 的響應碼,意思是,“我已經完成了你要求我做的事情,一切都正常”。響應碼總是一個三位數字的代號,web 應用在每個響應的同時都發送一個這樣的代號,表明給定的請求的結果。響應碼 200 字面意思是“OK”,是響應一個 GET 請求大多情況下都使用的代號。然而對于 POST 請求, 可能會有 204(“No Content”)發送回來,意思是“一切都正常,但是我不準備向你顯示任何東西”。

POST 請求仍然會發送一個特殊的 URL,這個 URL 可能和提交數據的頁面不同,意識這一點是至關重要的。還是以我們的登錄為例,表單可能是在 www.foo.com/signup

頁面,然而點擊 submit

,可能會導致帶有表單數據的 POST 請求發送到 www.foo.com/process_sigup

上。POST 請求要發送的位置在表單的 HTML 中有特別標明。

Web 應用

你可以僅僅使用 HTTP GET 和 POST 做很多事情。一個應用程序負責去接收一個 HTTP 請求,同時給以 HTTP 響應,通常包含了請求頁面的 HTML。POST 請求會引起 web 應用做出一些行為,可能是往數據庫中添加一條記錄這樣的。還有很多其它的 HTTP 方法,但是我們目前只關注 GET 和 POST。

那么最簡單的 web 應用是什么樣的呢?我們可以寫一個應用,讓它一直監聽 80 端口(著名的 HTTP 端口,幾乎所有 HTTP 都發送到這個端口上)。一旦它接收到等待的客戶端發送的請求連接,然后它就會回復一些簡單的 HTML。

下面是程序的代碼:

import socketHOST = ''PORT = 80listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.bind((HOST, PORT))listen_socket.listen(1)connection, address = listen_socket.accept()request = connection.recv(1024)connection.sendall(b"""HTTP/1.1 200 OKContent-type: text/html

Hello, World!

""")connection.close()

(如果上面的代碼不工作,試著將 PORT 改為類似 8080 這樣的端口。)

這個代碼接收簡單的鏈接和簡單的請求,不管請求的 URL 是什么,它都會響應 HTTP 200(所以,這不是一個真正意義上的 web 服務器)。Content-type:text/html

行代碼的是 header 字段,header 用來提供請求或者響應的元信息。這樣,我們就告訴了客戶端接下來的數據是 HTML。

請求的剖析

如果看一下測試上面程序的 HTTP 請求,你會發現它和 HTTP 響應非常類似。第一行

,在這個例子中是 GET / HTTP/1.0

。第一行之后就是一些類似Accept:/

這樣的頭(意思是我們希望在響應中接收任何內容)。

我們響應和請求有著類似的第一行,格式是

,在外面的例子中是HTTP/1.1 200 OK

。接下來是頭部,與請求的頭部有著相同的格式。最后是響應的實際包含的內容。注意,這會被解釋為一個字符串或者二進制文件,Content-type頭告訴客戶端怎樣去解釋響應。

Web 服務器之殤

如果我們繼續以上面的例子為基礎建立 web 應用,我們還需要解決很多問題:

我們怎樣檢測請求的 URL 以及返回正確的頁面?

除了簡單的 GET 請求之外我們如何處理 POST 請求?

我們如何理解更高級的概念,如 session 和 cookie?

我們如何擴展程序以使其處理上千個并發連接?

就像你想的那樣,沒有人愿意每次建立一個 web 應用都要解決這些問題。正是這個原因,就有處理 HTTP 協議本身和有效解決上面問題的辦法的包存在。然而,記住了,它們的核心功能和我們的例子是相同的:監聽請求,帶有一些 HTML 發回 HTTP 響應。

解決兩大問題:路由和模板

圍繞建立 web 應用的所有問題中,兩個問題尤其突出:

我們如何將請求的 URL 映射到處理它的代碼上?

我們怎樣動態地構造請求的 HTML 返回給客戶端,HTML 中帶有計算得到的值或者從數據庫中取出來的信息?

每個 web 框架都以某種方法來解決這些問題,也有很多不同的解決方案。用例子來說明更容易理解,所以我將針對這些問題討論 Django 和 Flask 的解決方案。但是,首先我們還需要簡單討論一下 MVC 。

Django 中的 MVC

Django 充分利用 MVC 設計模式。 MVC,也就是Model-View-Controller (模型-視圖-控制器),是一種將應用的不同功能從邏輯上劃分開。models 代表的是類似數據庫表的資源(與 Python 中用class來對真實世界目標建模使用的方法大體相同)。controls 包括應用的業務邏輯,對 models 進行操作。為了動態生成代表頁面的 HTML,需要 views 給出所有要動態生成頁面的 HTML 的信息。

在 Django 中有點讓人困惑的是,controllers 被稱做 views,而 views 被稱為 templates。除了名字上的有點奇怪,Django 很好地實現了 MVC 的體系架構。

Django 中的路由

路由是處理請求 URL 到負責生成相關的 HTML 的代碼之間映射的過程。在簡單的情形下,所有的請求都是有相同的代碼來處理(就像我們之前的例子那樣)。變得稍微復雜一點,每個 URL 對應一個 view function 。舉例來說,如果請求 www.foo.com/bar

這樣的 URL,調用 handler_bar()

這樣的函數來產生響應。我們可以建立這樣的映射表,枚舉出我們應用支持的所有 URL 與它們相關的函數。

然而,當 URL 中包含有用的數據,例如資源的 ID(像這樣 www.foo.com/users/3/

) ,那么這種方法將變得非常臃腫。我們如何將 URL 映射到一個 view 函數,同時如何利用我們想顯示 ID 為 3 的用戶?

Django 的答案是,將 URL 正則表達式映射到可以帶參數的 view 函數。例如,我假設匹配^/users/(?P\d+)/$

的 URL 調用 display_user(id)

這樣的函數,這兒參數 id 是正則表達式中匹配的 id。這種方法,任何 /users//

這樣的 URL 都會映射到 display_user

函數。這些正則表達式可以非常復雜,包含關鍵字和參數。

Flask 中的路由

Flask 采取了一點不同的方法。將一個函數和請求的 URL 關聯起來的標準方法是通過使用 route()

裝飾器。下面是 Flask 代碼,在功能上和上面正則表達式方法相同:

@app.route('/users//')def display_user(id): # ...

就像你看到的這樣,裝飾器使用幾乎最簡單的正則表達式的形式來將 URL 映射到參數。通過傳遞給route()

的 URL 中包含的

指令,可以提取到參數。路由像 /info/about_us.html

這樣的靜態 URL,可以像你預想的這樣 @app.route('/info/about_us.html')

處理。

通過 Templates 產生 HTML

繼續上面的例子,一旦我們有合適的代碼映射到正確的 URL,我們如何動態生成 HTML?對于 Django 和 Flask,答案都是通過HTML Templating

HTML Templating 和使用 str.format()

類似:需要動態輸出值的地方使用占位符填充,這些占位符后來通過 str.format()

函數用參數替換掉。想象一下,整個 web 頁面就是一個字符串,用括號標明動態數據的位置,最后再調用 str.format()

。Django 模板和 Flask 使用的模板引擎 Jinja2 都使用的是這種方法。

然而,不是所有的模板引擎都能相同的功能。Django 支持在模板里基本的編程,而 Jinja2 只能讓你執行特定的代碼(不是真正意義上的代碼,但也差不多)。Jinja2 可以緩存渲染之后的模板,讓接下來具有相同參數的請求可以直接從緩存中返回結果,而不是用再次花大力氣渲染。

數據庫交互

Django 有著“功能齊全”的設計哲學,其中包含了一個 ORM(ObjectRealationalMapper,對象關系映射),ORM 的目的有兩方面:一是將 Python 的 class 與數據庫表建立映射,而是剝離出不同數據庫引擎直接的差異。沒人喜歡 ORM,因為在不同的域之間映射永遠不完美,然而這還在承受范圍之內。Django 是功能齊全的,而 Flask 是一個微框架,不包括 ORM,盡管它對 SQLAlchemy 兼容性非常好,SQLAlchemy 是 Django ORM 的最大也是唯一的競爭對手。

內嵌 ORM 讓 Django 有能力創建一個功能豐富的 CRUD 應用,從服務器端角度來看,CRUDCreateReadUpdateDelete)應用非常適合使用 web 框架技術。Django 和 Flask-SQLchemy 可以直接對每個 model 進行不同的 CRUD 操作。

再談 web 框架

到現在為止,web 框架的目的應該非常清晰了:向程序員隱藏了處理 HTTP 請求和響應相關的基礎代碼。至于隱藏多少這取決于不同的框架,Django 和 Flask 走向了兩個極端:Django 包括了每種情形,幾乎成了它致命的一點;Flask 立足于“微框架”,僅僅實現 web 應用需要的最小功能,其它的不常用的 web 框架任務交由第三方庫來完成。

但是最后要記住的是,Python web 框架都以相同的方式工作的:它們接收 HTTP 請求,分派代碼,產生 HTML,創建帶有內容的 HTTP 響應。事實上,所有主流的服務器端框架都以這種方式工作的( JavaScript 框架除外)。但愿了解了這些框架的目的,你能夠在不同的框架之間選擇適合你應用的框架進行開發。


英文原版

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容

  • 本文系轉載于網絡。 Web 應用框架,或者簡單的說是“Web 框架”,其實是建立 web 應用的一種方式。從簡單的...
    everfight閱讀 298評論 0 3
  • 22年12月更新:個人網站關停,如果仍舊對舊教程有興趣參考 Github 的markdown內容[https://...
    tangyefei閱讀 35,200評論 22 257
  • 可不要被名字迷惑,它可不是web網頁的框架,而是服務器用來產生web網頁時用到的工具。 Web應用框架(簡稱Web...
    約伯閱讀 3,785評論 0 3
  • 我們常用的web前端框架其實簡單稱呼叫web框架,現階段web前端技術成熟,從視覺體驗到用戶體驗都是比較好的,這也...
    UIleader閱讀 1,696評論 0 2
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,787評論 18 139