伺服器端 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/myteamname/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)
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,包括:Uber、Accenture、IBM 等。
Deno (JavaScript)
Deno 是一個簡單、現代且安全的JavaScript/TypeScript 執行時和框架,構建在 Chrome V8 和Rust之上。
Deno 由Tokio提供支援——一個基於 Rust 的非同步執行時,它可以更快地提供 Web 頁面。它還內部支援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)
ASP.NET
ASP.NET 是由微軟開發的開源 Web 框架,用於構建現代 Web 應用程式和服務。使用 ASP.NET,您可以快速建立基於 HTML、CSS 和 JavaScript 的網站,將其擴充套件以供數百萬使用者使用,並輕鬆新增更復雜的功能,如 Web API、資料表單或即時通訊。
ASP.NET 的一個區別在於它構建在公共語言執行時(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(長輪詢)、保持活動、連線池、超時、Cookie、多部分和 gzip 壓縮。
- JSON 和 HTML/XML 解析器和生成器,支援 CSS 選擇器。
- 非常簡潔、可移植且面向物件的純 Perl API,沒有隱藏的魔法。
- 基於多年經驗的新程式碼,免費且開源。
Spring Boot (Java)
Spring Boot 是 Spring 提供的眾多專案之一。它是使用 Java 進行伺服器端 Web 開發的一個良好起點。
雖然它絕對不是唯一一個基於 Java 的框架,但它易於使用,可以建立獨立的、生產級的基於 Spring 的應用程式,你可以“直接執行”。它對 Spring 平臺和第三方庫持有特定的觀點,但允許你以最少的麻煩和配置開始。
它可以用於解決小問題,但其優勢在於構建使用雲方法的大規模應用程式。通常,多個應用程式並行執行並相互通訊,其中一些提供使用者互動,另一些執行後端工作(例如訪問資料庫或其他服務)。負載均衡器有助於確保冗餘性和可靠性,或允許對使用者請求進行地理位置處理以確保響應能力。