服务器端 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/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)、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,包括:优步、埃森哲、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 以及将数据格式化为 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 的一个独特之处在于它构建在 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 BootSpring 提供的众多项目之一。它是使用 Java 进行服务器端 Web 开发的良好起点。

虽然它绝不是唯一基于 Java 的框架,但它易于使用,可以创建独立的、生产级的基于 Spring 的应用程序,你可以“直接运行”。它对 Spring 平台和第三方库有明确的看法,但允许以最少的麻烦和配置开始。

它可用于解决小问题,但其强项是构建采用云方法的大规模应用程序。通常多个应用程序并行运行,相互通信,其中一些提供用户交互,另一些进行后端工作(例如,访问数据库或其他服务)。负载均衡器有助于确保冗余和可靠性,或允许对用户请求进行地理位置处理以确保响应性。

总结

本文已表明 Web 框架可以使服务器端代码的开发和维护变得更容易。它还提供了对一些流行框架的高级概述,并讨论了选择 Web 应用程序框架的标准。你现在应该至少对如何为自己的服务器端开发选择 Web 框架有了一个概念。如果没有,请不要担心——在本课程的后面,我们将为你提供关于 Django 和 Express 的详细教程,让你实际体验 Web 框架的工作。

在本模块的下一篇文章中,我们将稍微改变方向并考虑 Web 安全。