伺服器端 Web 框架
上一篇文章向你展示了 Web 客戶端和伺服器之間的通訊方式,HTTP 請求和響應的性質,以及伺服器端 Web 應用程式需要做些什麼來響應來自 Web 瀏覽器的請求。有了這些知識,是時候探索 Web 框架如何簡化這些任務了,並讓你瞭解如何為你的第一個伺服器端 Web 應用程式選擇一個框架。
| 預備知識 | 基本瞭解伺服器端程式碼如何處理和響應 HTTP 請求(參見客戶端-伺服器概述)。 |
|---|---|
| 目標 | 瞭解 Web 框架如何簡化伺服器端程式碼的開發/維護,並讓讀者開始思考為自己的開發選擇一個框架。 |
以下部分將使用來自真實 Web 框架的程式碼片段來闡述一些觀點。如果現在不是所有都能理解,請不要擔心;我們將在框架特定的模組中為你講解程式碼。
概述
伺服器端 Web 框架(也稱為“Web 應用程式框架”)是使編寫、維護和擴充套件 Web 應用程式更容易的軟體框架。它們提供工具和庫來簡化常見的 Web 開發任務,包括將 URL 路由到適當的處理程式、與資料庫互動、支援會話和使用者授權、格式化輸出(例如 HTML、JSON、XML)以及提高針對 Web 攻擊的安全性。
下一節將更詳細地介紹 Web 框架如何簡化 Web 應用程式開發。然後,我們解釋了選擇 Web 框架時可以使用的標準,並列出了一些選項。
Web 框架能為你做什麼?
Web 框架提供工具和庫來簡化常見的 Web 開發操作。你不是必須使用伺服器端 Web 框架,但強烈建議使用——它會讓你的工作輕鬆很多。
本節討論了 Web 框架通常提供的一些功能(並非每個框架都必然提供所有這些功能!)。
直接處理 HTTP 請求和響應
正如我們在上一篇文章中看到的,Web 伺服器和瀏覽器透過 HTTP 協議進行通訊——伺服器等待來自瀏覽器的 HTTP 請求,然後以 HTTP 響應返回資訊。Web 框架允許你編寫簡化的語法,該語法將生成伺服器端程式碼來處理這些請求和響應。這意味著你的工作將變得更容易,透過更容易、更高級別的程式碼而不是更低級別的網路原語進行互動。
下面的示例展示了這在 Django (Python) Web 框架中是如何工作的。每個“檢視”函式(請求處理程式)都會接收一個包含請求資訊的 HttpRequest 物件,並且需要返回一個帶有格式化輸出(在本例中為字串)的 HttpResponse 物件。
# Django view function
from django.http import HttpResponse
def index(request):
# Get an HttpRequest (request)
# perform operations using information from the request.
# Return HttpResponse
return HttpResponse('Output string to return')
將請求路由到適當的處理程式
大多數站點都會提供許多不同的資源,可透過不同的 URL 訪問。將所有這些都放在一個函式中處理將很難維護,因此 Web 框架提供了簡單的機制來將 URL 模式對映到特定的處理函式。這種方法在維護方面也有好處,因為你可以更改用於交付特定功能的 URL,而無需更改底層程式碼。
不同的框架使用不同的對映機制。例如,Flask (Python) Web 框架使用裝飾器將路由新增到檢視函式。
@app.route("/")
def hello():
return "Hello World!"
而 Django 則要求開發者定義 URL 模式和檢視函式之間的一系列 URL 對映。
urlpatterns = [
url(r'^$', views.index),
# example: /best/my_team_name/5/
url(r'^best/(?P<team_name>\w+?)/(?P<team_number>[0-9]+)/$', views.best),
]
輕鬆訪問請求中的資料
資料可以透過多種方式在 HTTP 請求中編碼。一個用於從伺服器獲取檔案或資料的 HTTP GET 請求可能在 URL 引數中或 URL 結構中編碼所需的資料。一個用於更新伺服器上資源的 HTTP POST 請求則會在請求正文中包含更新資訊作為“POST 資料”。HTTP 請求還可以在客戶端 cookie 中包含有關當前會話或使用者的資訊。
Web 框架提供適合程式語言的機制來訪問這些資訊。例如,Django 傳遞給每個檢視函式的 HttpRequest 物件包含用於訪問目標 URL、請求型別(例如 HTTP GET)、GET 或 POST 引數、cookie 和會話資料等的方法和屬性。Django 還可以透過在 URL 對映器中定義“捕獲模式”(參見上一節中的最後一個程式碼片段)來傳遞編碼在 URL 結構中的資訊。
抽象和簡化資料庫訪問
網站使用資料庫來儲存要與使用者共享的資訊以及有關使用者的資訊。Web 框架通常提供一個數據庫層,用於抽象資料庫的讀取、寫入、查詢和刪除操作。這個抽象層被稱為物件關係對映器(ORM)。
使用 ORM 有兩個好處
- 你可以替換底層資料庫,而無需更改使用它的程式碼。這允許開發人員根據不同資料庫的特性進行最佳化。
- 基本資料驗證可以在框架內部實現。這使得檢查資料是否儲存在正確型別的資料庫欄位中、是否具有正確的格式(例如,電子郵件地址)以及是否不包含任何惡意內容(駭客可以使用某些程式碼模式來執行諸如刪除資料庫記錄之類的壞事)變得更容易和更安全。
例如,Django Web 框架提供了一個 ORM,並將用於定義記錄結構的物件稱為模型。模型指定要儲存的欄位型別,這可以提供欄位級別的驗證,以確定可以儲存哪些資訊(例如,電子郵件欄位只允許有效的電子郵件地址)。欄位定義還可以指定它們的最大大小、預設值、選擇列表選項、文件的幫助文字、表單的標籤文字等。模型不說明任何關於底層資料庫的資訊,因為那是一個可以獨立於我們程式碼更改的配置設定。
下面的第一個程式碼片段展示了一個非常簡單的 Django Team 物件模型。它將團隊名稱和團隊級別儲存為字元欄位,並指定每個記錄要儲存的最大字元數。team_level 是一個選擇欄位,因此我們還提供了要顯示的選項和要儲存的資料之間的對映,以及一個預設值。
#best/models.py
from django.db import models
class Team(models.Model):
team_name = models.CharField(max_length=40)
TEAM_LEVELS = (
('U09', 'Under 09s'),
('U10', 'Under 10s'),
('U11', 'Under 11s'),
# List our other teams
)
team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
Django 模型提供了一個簡單的查詢 API,用於搜尋資料庫。它可以根據多個欄位同時使用不同的條件(例如,精確匹配、不區分大小寫、大於等等),並且可以支援複雜的語句(例如,你可以指定搜尋 U11 團隊,其團隊名稱以“Fr”開頭或以“al”結尾)。
第二個程式碼片段顯示了一個檢視函式(資源處理程式),用於顯示我們所有的 U09 團隊。在這種情況下,我們指定要過濾所有 team_level 欄位恰好包含文字“U09”的記錄(請注意下面此條件如何作為引數傳遞給 filter() 函式,欄位名和匹配型別由雙下劃線分隔:team_level__exact)。
#best/views.py
from django.shortcuts import render
from .models import Team
def youngest(request):
list_teams = Team.objects.filter(team_level__exact="U09")
context = {'youngest_teams': list_teams}
return render(request, 'best/index.html', context)
資料渲染
Web 框架通常提供模板系統。這些系統允許你指定輸出文件的結構,使用佔位符來表示在生成頁面時將新增的資料。模板通常用於建立 HTML,但也可以建立其他型別的文件。
Web 框架通常提供一種機制,可以輕鬆地從儲存的資料生成其他格式,包括 JSON 和 XML。
例如,Django 模板系統允許你使用“雙大括號”語法(例如,{{ variable_name }})指定變數,這些變數在頁面渲染時將被檢視函式傳入的值替換。模板系統還支援表示式(語法為:{% expression %}),允許模板執行簡單的操作,例如迭代傳入模板的列表值。
注意:許多其他模板系統也使用類似的語法,例如:Jinja2 (Python)、handlebars (JavaScript)、moustache (JavaScript) 等。
下面的程式碼片段展示了它的工作原理。繼續上一節中“最年輕團隊”的示例,HTML 模板被檢視傳入一個名為 youngest_teams 的列表變數。在 HTML 骨架內部,我們有一個表示式,它首先檢查 youngest_teams 變數是否存在,然後在一個 for 迴圈中對其進行迭代。在每次迭代中,模板在一個列表項中顯示團隊的 team_name 值。
#best/templates/best/index.html
<!doctype html>
<html lang="en">
<body>
{% if youngest_teams %}
<ul>
{% for team in youngest_teams %}
<li>{{ team.team_name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No teams are available.</p>
{% endif %}
</body>
</html>
如何選擇 Web 框架
幾乎每種你想使用的程式語言都有大量的 Web 框架(我們在下一節中列出了一些更流行的框架)。面對如此多的選擇,要找出哪個框架能為你的新 Web 應用程式提供最佳起點可能會變得很困難。
可能影響你決定的一些因素是
-
學習難度:學習一個 Web 框架的難度取決於你對底層程式語言的熟悉程度、其 API 的一致性、其文件的質量以及其社群的規模和活躍度。如果你完全沒有程式設計經驗,那麼考慮 Django(根據上述標準,它是最容易學習的框架之一)。如果你是開發團隊的一員,並且已經對某個特定的 Web 框架或程式語言有豐富的經驗,那麼堅持使用它是有意義的。
-
生產力:生產力衡量的是一旦你熟悉了框架,你能夠多快地建立新功能,它包括編寫和維護程式碼的精力(因為如果舊功能損壞了,你就無法編寫新功能)。許多影響生產力的因素與“學習難度”相似——例如,文件、社群、程式設計經驗等——其他因素包括
- 框架目的/起源:一些 Web 框架最初是為了解決某些型別的問題而建立的,並且在建立具有類似限制的 Web 應用程式方面仍然更擅長。例如,Django 是為了支援報社網站的開發而建立的,因此它非常適合部落格和其他涉及釋出內容的網站。相比之下,Flask 是一個輕量得多的框架,非常適合建立在嵌入式裝置上執行的 Web 應用程式。
- 有主見 vs. 無主見:有主見的框架是指那些有推薦的“最佳”方式來解決特定問題的框架。有主見的框架在解決常見問題時往往效率更高,因為它們會引導你走向正確的方向,但有時它們缺乏靈活性。
- 內建電池 vs. 自行獲取:有些 Web 框架“預設”包含解決其開發人員能想到的所有問題的工具/庫,而更輕量級的框架則期望 Web 開發人員從獨立的庫中選擇解決方案(Django 是前者的一個例子,而 Flask 是一個非常輕量級框架的例子)。包含所有內容的框架通常更容易上手,因為你已經擁有所需的一切,並且很有可能它們已經很好地整合並有完善的文件。然而,如果一個較小的框架擁有你(將來)需要的一切,那麼它可以在更受限制的環境中執行,並且學習內容更少、更簡單。
- 框架是否鼓勵良好的開發實踐:例如,鼓勵 模型-檢視-控制器 架構以將程式碼分離為邏輯函式的框架將比對開發人員沒有期望的框架產生更易於維護的程式碼。同樣,框架設計對程式碼的測試和重用難易程度也有很大影響。
-
框架/程式語言的效能:通常,“速度”並不是選擇中最大的因素,因為即使是像 Python 這樣相對較慢的執行時,對於執行在中等硬體上的中型站點也“足夠好”。另一種語言(例如 C++ 或 JavaScript)所感知的速度優勢很可能會被學習和維護成本所抵消。
-
快取支援:當你的網站越來越成功時,你可能會發現它無法應對使用者訪問時收到的請求數量。此時,你可以考慮新增快取支援。快取是一種最佳化,你儲存 Web 響應的全部或部分,以便在後續請求中不必重新計算。返回快取的響應比第一次計算要快得多。快取可以在你的程式碼中實現,也可以在伺服器中實現(參見反向代理)。Web 框架將對定義哪些內容可以快取提供不同級別的支援。
-
可擴充套件性:一旦你的網站取得了巨大的成功,你將用盡快取的好處,甚至達到垂直擴充套件的極限(在更強大的硬體上執行你的 Web 應用程式)。此時,你可能需要水平擴充套件(透過將你的網站分佈到多個 Web 伺服器和資料庫上來分擔負載)或“地理”擴充套件,因為你的一些客戶離你的伺服器很遠。你選擇的 Web 框架對你的網站擴充套件的難易程度有很大的影響。
-
Web 安全:一些 Web 框架為處理常見的 Web 攻擊提供了更好的支援。例如,Django 會清理 HTML 模板中所有使用者輸入,以防止使用者輸入的 JavaScript 執行。其他框架也提供類似的保護,但並非總是預設啟用。
還有許多其他可能的因素,包括許可、框架是否處於積極開發中等等。
如果你是程式設計新手,那麼你可能會根據“易學性”來選擇框架。除了語言本身的“易用性”之外,高質量的文件/教程和活躍的社群幫助新使用者是你最寶貴的資源。我們選擇了 Django (Python) 和 Express (Node/JavaScript) 來編寫本課程後面的示例,主要是因為它們易於學習且支援良好。
有哪些不錯的 Web 框架?
現在,我們繼續討論一些特定的伺服器端 Web 框架。
以下伺服器端框架代表了撰寫本文時最受歡迎的少數框架。它們都具備讓你高效工作所需的一切——它們是開源的,正在積極開發中,擁有熱情的社群建立文件並在討論區幫助使用者,並被大量知名網站使用。透過基本的網際網路搜尋,你可以發現許多其他優秀的伺服器端框架。
注意:描述(部分)來自框架網站!
Django (Python)
Django 是一個高階 Python Web 框架,鼓勵快速開發和簡潔、實用的設計。它由經驗豐富的開發人員構建,解決了 Web 開發中的大部分繁瑣工作,因此你可以專注於編寫應用程式,而無需重複造輪子。它是免費和開源的。
Django 遵循“內建電池”理念,開箱即用,提供大多數開發人員可能想要做的幾乎所有事情。由於一切都包含在內,因此它們協同工作,遵循一致的設計原則,並擁有廣泛且最新的文件。它還快速、安全且高度可擴充套件。基於 Python,Django 程式碼易於閱讀和維護。
使用 Django 的熱門網站(來自 Django 主頁)包括:Disqus、Instagram、Knight Foundation、MacArthur Foundation、Mozilla、National Geographic、Open Knowledge Foundation、Pinterest、Open Stack。
Flask (Python)
Flask 是一個 Python 微框架。
雖然極簡,Flask 仍可以開箱即用建立嚴肅的網站。它包含一個開發伺服器和偵錯程式,並支援 Jinja2 模板、安全 cookie、單元測試 和 RESTful 請求分派。它有良好的文件和活躍的社群。
Flask 變得非常流行,特別是對於需要在小型、資源受限的系統上提供 Web 服務的開發人員(例如,在 Raspberry Pi、無人機控制器 等上執行 Web 伺服器)。
Express (Node.js/JavaScript)
Express 是一個用於 Node.js(Node 是一個用於執行 JavaScript 的無瀏覽器環境)的快速、無偏見、靈活且極簡的 Web 框架。它為 Web 和移動應用程式提供了一套強大的功能,並提供了有用的 HTTP 工具方法和 中介軟體。
Express 非常流行,部分原因在於它簡化了客戶端 JavaScript Web 程式設計師向伺服器端開發的遷移,部分原因在於它資源高效(底層 node 環境在一個執行緒中利用輕量級多工處理,而不是為每個新的 Web 請求派生單獨的程序)。
由於 Express 是一個極簡的 Web 框架,它不包含你可能想要使用的所有元件(例如,資料庫訪問和對使用者和會話的支援是透過獨立的庫提供的)。有許多優秀的獨立元件,但有時很難確定哪個最適合特定用途!
許多流行的伺服器端和全棧框架(包括伺服器端和客戶端框架)都基於 Express,包括 Feathers、ItemsAPI、KeystoneJS、Kraken、LoopBack、MEAN 和 Sails。
許多知名公司使用 Express,包括:優步、埃森哲、IBM 等。
Deno (JavaScript)
Deno 是一個簡單、現代且安全的 JavaScript/TypeScript 執行時和框架,構建於 Chrome V8 和 Rust 之上。
Deno 由 Tokio 提供支援——一個基於 Rust 的非同步執行時,使其能夠更快地提供網頁。它還內部支援 WebAssembly,這使得二進位制程式碼可以編譯用於客戶端。Deno 旨在透過提供一種自然地維護更好安全性的機制來填補 Node.js 中的一些漏洞。
Deno 的特點包括
- 預設安全。Deno 模組限制了對檔案、網路或環境的訪問許可權,除非明確允許。
- 開箱即用的 TypeScript 支援。
- 一流的 await 機制。
- 內建測試工具和程式碼格式化工具 (
deno fmt) - (JavaScript) 瀏覽器相容性:完全用 JavaScript 編寫的 Deno 程式(不包括
Deno名稱空間或對其進行功能測試)應該可以直接在任何現代瀏覽器中執行。 - 指令碼捆綁到一個 JavaScript 檔案中。
Deno 提供了一種簡單而強大的方式,將 JavaScript 用於客戶端和伺服器端程式設計。
Ruby on Rails (Ruby)
Rails(通常稱為“Ruby on Rails”)是一個為 Ruby 程式語言編寫的 Web 框架。
Rails 遵循與 Django 非常相似的設計理念。與 Django 一樣,它提供了用於路由 URL、從資料庫訪問資料、從模板生成 HTML 以及將資料格式化為 JSON 或 XML 的標準機制。它同樣鼓勵使用 DRY(“不要重複自己”——儘可能只編寫一次程式碼)、MVC(模型-檢視-控制器)等設計模式。
當然,由於特定的設計決策和語言的性質,存在許多差異。
Rails 已用於知名網站,包括:Basecamp、GitHub、Shopify、Airbnb、Twitch、SoundCloud、Hulu、Zendesk、Square、Highrise。
Laravel (PHP)
Laravel 是一個具有表現力、優雅語法的 Web 應用程式框架。Laravel 試圖透過簡化大多數 Web 專案中使用的常見任務來消除開發的痛苦,例如
Laravel 易於使用,但功能強大,為大型、健壯的應用程式提供了所需的工具。
ASP.NET
ASP.NET 是一個由微軟開發的開源 Web 框架,用於構建現代 Web 應用程式和服務。使用 ASP.NET,你可以快速建立基於 HTML、CSS 和 JavaScript 的網站,將其擴充套件以供數百萬使用者使用,並輕鬆新增更復雜的功能,如 Web API、基於資料的表單或即時通訊。
ASP.NET 的一個獨特之處在於它構建在 Common Language Runtime (CLR) 上,允許程式設計師使用任何受支援的 .NET 語言(C#、Visual Basic 等)編寫 ASP.NET 程式碼。與許多微軟產品一樣,它受益於出色的工具(通常是免費的)、活躍的開發社群和編寫精良的文件。
ASP.NET 被微軟、Xbox.com、Stack Overflow 和許多其他公司使用。
Mojolicious (Perl)
Mojolicious 是一個用於 Perl 程式語言的下一代 Web 框架。
在 Web 早期,許多人學習 Perl 是因為一個優秀的 Perl 庫叫做 CGI。它足夠簡單,即使不瞭解太多語言知識也能上手,並且足夠強大,能讓你繼續使用。Mojolicious 使用尖端技術實現了這一理念。
Mojolicious 提供的一些功能是
- 一個即時 Web 框架,可以輕鬆地將單檔案原型發展為結構良好的 MVC Web 應用程式。
- RESTful 路由、外掛、命令、Perl 風格模板、內容協商、會話管理、表單驗證、測試框架、靜態檔案伺服器、CGI/PSGI 檢測和一流的 Unicode 支援。
- 一個全棧 HTTP 和 WebSocket 客戶端/伺服器實現,支援 IPv6、TLS、SNI、IDNA、HTTP/SOCKS5 代理、UNIX 域套接字、Comet (長輪詢)、keep-alive、連線池、超時、cookie、multipart 和 gzip 壓縮。
- JSON 和 HTML/XML 解析器和生成器,支援 CSS 選擇器。
- 非常乾淨、可移植且面向物件的純 Perl API,沒有隱藏的魔法。
- 基於多年經驗的新程式碼,免費且開源。
Spring Boot (Java)
Spring Boot 是 Spring 提供的眾多專案之一。它是使用 Java 進行伺服器端 Web 開發的良好起點。
雖然它絕不是唯一基於 Java 的框架,但它易於使用,可以建立獨立的、生產級的基於 Spring 的應用程式,你可以“直接執行”。它對 Spring 平臺和第三方庫有明確的看法,但允許以最少的麻煩和配置開始。
它可用於解決小問題,但其強項是構建採用雲方法的大規模應用程式。通常多個應用程式並行執行,相互通訊,其中一些提供使用者互動,另一些進行後端工作(例如,訪問資料庫或其他服務)。負載均衡器有助於確保冗餘和可靠性,或允許對使用者請求進行地理位置處理以確保響應性。
總結
本文已表明 Web 框架可以使伺服器端程式碼的開發和維護變得更容易。它還提供了對一些流行框架的高階概述,並討論了選擇 Web 應用程式框架的標準。你現在應該至少對如何為自己的伺服器端開發選擇 Web 框架有了一個概念。如果沒有,請不要擔心——在本課程的後面,我們將為你提供關於 Django 和 Express 的詳細教程,讓你實際體驗 Web 框架的工作。
在本模組的下一篇文章中,我們將稍微改變方向並考慮 Web 安全。