服务器端 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 安全性。