服务器端 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 平台和第三方库持有特定的观点,但允许你以最少的麻烦和配置开始。
它可以用于解决小问题,但其优势在于构建使用云方法的大规模应用程序。通常,多个应用程序并行运行并相互通信,其中一些提供用户交互,另一些执行后端工作(例如访问数据库或其他服务)。负载均衡器有助于确保冗余性和可靠性,或允许对用户请求进行地理位置处理以确保响应能力。