伺服器端 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物件。

python
# 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 框架使用裝飾器將路由新增到檢視函式。

python
@app.route("/")
def hello():
    return "Hello World!"

而 Django 則期望開發人員定義一個 URL 對映列表,該列表將 URL 模式與檢視函式關聯起來。

python
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)、GETPOST 引數、cookie 和會話資料等的方法和屬性。Django 還可以透過在 URL 對映器中定義“捕獲模式”來傳遞編碼在 URL 結構中的資訊(請參見上一節中的最後一個程式碼片段)。

抽象和簡化資料庫訪問

網站使用資料庫來儲存資訊,這些資訊既可以與使用者共享,也可以與使用者相關。Web 框架通常提供一個數據庫層,該層抽象了資料庫的讀取、寫入、查詢和刪除操作。此抽象層稱為物件關係對映 (ORM)。

使用 ORM 有兩個好處

  • 您可以替換底層資料庫,而無需更改使用它的程式碼。這允許開發人員根據其使用情況最佳化不同資料庫的特性。
  • 可以在框架內實現基本的資料驗證。這使得檢查資料是否儲存在正確型別的資料庫欄位中、是否具有正確的格式(例如電子郵件地址)以及是否以任何方式具有惡意性變得更容易和更安全(駭客可以使用某些程式碼模式來執行有害操作,例如刪除資料庫記錄)。

例如,Django Web 框架提供了一個 ORM,並將用於定義記錄結構的物件稱為模型。模型指定要儲存的欄位型別,這些型別可能會對可以儲存的資訊進行欄位級驗證(例如,電子郵件欄位將僅允許有效的電子郵件地址)。欄位定義還可以指定其最大大小、預設值、選擇列表選項、文件的幫助文字、表單的標籤文字等。模型不會宣告任何有關底層資料庫的資訊,因為這是可以與我們的程式碼分開更改的配置設定。

下面的第一個程式碼片段顯示了一個非常簡單的 Django 模型,用於Team物件。它將團隊名稱和團隊級別儲存為字元欄位,並指定每個記錄要儲存的最大字元數。team_level是一個選擇欄位,因此我們還提供了一個對映,用於在要顯示的選擇和要儲存的資料之間進行對映,以及一個預設值。

python
#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)。

python
#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 框架通常提供一種機制,可以輕鬆地從儲存的資料生成其他格式,包括JSONXML

例如,Django 模板系統允許您使用“雙花括號”語法(例如{{ variable_name }})指定變數,這些變數將在渲染頁面時由檢視函式傳遞的值替換。模板系統還支援表示式(語法:{% expression %}),這些表示式允許模板執行簡單的操作,例如迭代傳遞到模板的列表值。

注意:許多其他模板系統使用類似的語法,例如:Jinja2(Python)、Handlebars(JavaScript)、Moustache(JavaScript)等。

下面的程式碼片段顯示了它是如何工作的。繼續上一節中“最年輕的團隊”示例,HTML 模板透過檢視傳遞了一個名為youngest_teams的列表變數。在 HTML 骨架內部,我們有一個表示式,首先檢查youngest_teams變數是否存在,然後在for迴圈中迭代它。在每次迭代中,模板都會在列表項中顯示團隊的team_name值。

django
#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)來編寫我們在課程後面的示例,主要是因為它們易於學習並且有良好的支援。

注意:讓我們訪問Django(Python)和Express(Node/JavaScript)的主要網站,並檢視它們的文件和社群。

  1. 導航到主要網站(上面連結)。
    • 點選文件菜單鏈接(命名為“文件”、“指南”、“API 參考”、“入門”等)。
    • 您能否看到顯示如何設定 URL 路由、模板和資料庫/模型的主題?
    • 文件是否清晰?
  2. 導航到每個網站的郵件列表(可以透過“社群”連結訪問)。
    • 過去幾天釋出了多少個問題?
    • 有多少個問題得到了回覆?
    • 它們是否有活躍的社群?

一些優秀的 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,包括FeathersItemsAPIKeystoneJSKrakenLoopBackMEANSails

許多高知名度的公司使用 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 以及將資料格式化為JSONXML的標準機制。它同樣鼓勵使用設計模式,如 DRY(“不要重複自己”——如果可能,只編寫一次程式碼)、MVC(模型-檢視-控制器)和許多其他模式。

當然,由於特定的設計決策和語言的性質,存在許多差異。

Rails 已被用於高知名度的網站,包括:BasecampGitHubShopifyAirbnbTwitchSoundCloudHuluZendeskSquareHighrise

Laravel (PHP)

Laravel 是一個具有表達性、優雅語法的 Web 應用程式框架。Laravel 試圖透過簡化大多數 Web 專案中使用的常見任務來減輕開發工作,例如

Laravel 易於訪問,但功能強大,提供了大型、健壯應用程式所需的工具。

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 BootSpring 提供的眾多專案之一。它是使用 Java 進行伺服器端 Web 開發的一個良好起點。

雖然它絕對不是唯一一個基於 Java 的框架,但它易於使用,可以建立獨立的、生產級的基於 Spring 的應用程式,你可以“直接執行”。它對 Spring 平臺和第三方庫持有特定的觀點,但允許你以最少的麻煩和配置開始。

它可以用於解決小問題,但其優勢在於構建使用雲方法的大規模應用程式。通常,多個應用程式並行執行並相互通訊,其中一些提供使用者互動,另一些執行後端工作(例如訪問資料庫或其他服務)。負載均衡器有助於確保冗餘性和可靠性,或允許對使用者請求進行地理位置處理以確保響應能力。

總結

本文表明,Web 框架可以使開發和維護伺服器端程式碼變得更容易。它還提供了幾個流行框架的高階概述,並討論了選擇 Web 應用程式框架的標準。你現在至少應該瞭解如何為自己的伺服器端開發選擇 Web 框架。如果沒有,也不用擔心——稍後在本課程中,我們將為您提供 Django 和 Express 的詳細教程,讓您體驗實際使用 Web 框架。

在本模組的下一篇文章中,我們將稍微改變方向,考慮 Web 安全性。