前面的文章向你展示了web客户端和服务器之间的通信是什么样子的,HTTP的请求和回应之间的性质,以及服务器端应用为了回应来自web浏览器的请求的需要做的事情。有了这些知识后,现在是时候来探索一个web框架是如何简化这些任务的,并且告诉你应该如何为你的第一个服务器端应用选择一个框架。

预先要求: 基础电脑素养。对于服务器端代码是如何处理并响应HTTP请求有深刻的理解。(参见Client-Server overview
目标: 理解web框架是如何简化服务器端代码的开发和维护的,并且帮助读者思考如何为他们自己的开发项目选择一个框架。

下面的部分通过使用一些来自web框架的代码段来说明一些要点。如果不能完全看懂代码也不用太在意。我们在“框架详解”模块会帮助你完全理解。

概览

服务器端框架(亦称 "web 应用框架") 使编写、维护和扩展web应用更加容易。它们提供工具和库来实现简单、常见的开发任务, 包括 路由处理, 数据库交互, 会话支持和用户验证, 格式化输出 (e.g. HTML, JSON, XML), 提高安全性应对网络攻击.

下一节将详细介绍web框架如何简化web应用程序开发。然后,我将阐述一些选择web框架的标准,并给你列出一些选项。

web框架可以为你做什么?

你并不是必须得使用一个服务器端的web框架,但是我们强烈建议你使用框架——框架会使得你的生活更美好。

这个部分我们讲一下web框架通常会提供的功能(并不是说每一个框架一定会体供下面的所有功能!)

直接处理 HTTP 请求和响应

从上一篇文章中我们知道,web服务器和浏览器通过HTTP协议进行通信——服务器等待来自浏览器的HTTP请求然后在HTTP回应中返回相关信息。web框架允许你编写简单语法的代码,即可生成处理这些请求和回应的代码。这意味着你的工作变得简单、交互变得简单、并且使用抽象程度高的代码而不是底层代码。

每一个“view”函数(请求的处理者)接受一个包含请求信息的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')

将请求路由到相关的handler中

大多数的站点会提供一系列不同资源,通过特定的URL来访问。如果都放在一个函数里面,网站会变得很难维护。所以web框架提供一个简单机制来匹配URL和特定处理函数。这种方式对网站维护也有好处,因为你只需要改变用来传输特定功能的URL而不用改变任何底层代码。

不同的框架使用不同机制进行匹配。比如Flask(Python)框架通过使用装饰器来增加视图的路由。

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

然而,Django则期望开发者们定义一张URL pattern和视图函数URL的匹配列表。

urlpatterns = [
    url(r'^$', views.index),
    # example: /best/myteamname/5/
    url(r'^(?P<team_name>\w.+?)/(?P<team_number>[0-9]+)/$', views.best),
]

使从请求中获得数据变得简单

数据在HTTP请求中的编码方式有很多种。一个从服务器获得文件或者数据的HTTP GET请求可能会按照URL参数中要求的或者URL结构中的方式进行编码。一个更新服务器上数据的HTTP POST请求则会在请求主体中包含像“POST data”这样的更新信息。HTTP请求也可能包含客户端cookie中的即时会话和用户信息。

web框架提供一个获得这些信息的适合编程语言的机制。比如,Django传递给视图函数的HttpRequest对象包含着获得目标URL的方式和属性、请求的类型(比如一个HTTP GET)、GET或者POST参数、cookie或者session数据等等。Djamo也可以通过在URL匹配表中定义“抓取模式”来在URL结构中传递编码了的信息(如上面的编码片段中的最后一行)。

抽象和简化数据库接口

网站使用数据库来存储与用户分享的信息和用户个人信息。web框架通常会提供一个数据库层来抽象数据库的读、写、查询和删除操作。这一个抽象层被称作对象关系映射器(ORM)。

使用对象关系映射器有两个有点:

  • 你不需要改变使用数据库的代码就可以替换底层数据库。这就允许开发者依据用途优化不同数据库的特点。
  • 简单的数据的验证可以被植入到框架中。这会使得检查数据是否按照正确的方式存储在数据库字段中或者是否是特定的格式变得简单(比如邮箱地址),并且不是恶意的(黑客可以利用特定的编码模式来进行一些如删除数据库记录的非法操作)。

比如,Django框架提供一个对应关系映射,并且将用来定义数据库记录的结构称作模型。模型制定被存储的字段类型,可能也会提供那些要被存储的信息的验证(比如,一个email字段只允许合法email地址)。字段可能也会指明最大信息量、默认值、选项列表、帮助文档、表单标签等。这个模型不会申明任何底层数据库的信息,因为这是一个只能被我们的代码改变的配置信息。

下面第一个代码片段展示了一个简单的为Team对象设计的Django模型。这个模型会按照character字段来存储一个队伍的名字和级别,同时还指定了每一条记录存储的最大数量。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水平的队伍。在这种情况下我们指明我们我们想要过滤掉所有的字段中包含文本’U09‘的team_level字段(注意下面标准是如何作为一个变量,和字段名还有被双重下划线分离出的匹配类型(team_level__exact),传递到filter( )函数中去的)。

#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的,但是也可以用来生成一些其他的文档。

框架提供一个机制,使得从存储的数据中生成其他格式数据变得简单,包括JSONXML

比如,Django模板允许你通过使用“双重花括号”(如{{ variable_name }})来指定变量,当页面被渲染出来时,这些变量会被从视图函数传递过来的值代替。模板系统也会提供表达支持(通过语法{% expression %}来实现),这样就允许模板进行一些简单的操作比如迭代传递给模板的值列表。

Note: 很多其他的模板系统使用相似的语法,比如:Jinja2 (Python), handlebars (JavaScript), moustache (JavaScript), 等。

下面的代码片段展示了它们如何工作的。下面的内容接着从上一个部分而来的“youngest team”实例,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吧(它是基于上面几条标准来看最容易学习的了)。如果你已经成为开发团队的一部分,而那个开发团队对某一种语言或者某一个框架有着很重要的开发经历,那么就坚持相关框架。
  • 效率:效率是指一旦你熟悉某一个框架之后,你能够多块地创造一个新功能的衡量方式,包括编写和维护代码的代价(因为当前面的功能崩掉之后,你没法编写新的功能)。影响效率的大多数因素和学习代价是类似的——比如,文档,社区,编程经历等等。——其他因素还有:
    • 框架目的/起源:一些框架最初是用来解决某一类特定问题的,并且最好在生成app的时候顾及到这些约束。比如,Django是用来支持新闻网站的,因此非常适合博客或者其他包含发布内容的网站。相反的,Flask是一个相对来说轻量级的框架,因此适合用来生成一些在嵌入式设备上运行的app。
    • Opinionated vs unopinionated:一个opinionated的框架是说,解决某一个特定问题时,总有一个被推荐的最佳的解决方法。opinionated的框架在你试图解决一些普通问题的时候,更加趋向于产品化,因为它们会将你引入正确的方向,尽管有些时候并不那么灵活。
    • 一些web框架默认地包含了开发者们能遇到的任何一个问题的工具/库,而一些轻量级的框架希望开发者们自己从分离的库中选择合适的解决方式(Django是其前者的一个实例,而Flask则是轻量级的一个实例)。包含了所有东西的框架通常很容易上手因为你已经有了你所需要的任何东西,并且很可能它已经被整合好了,并且文档也写得很完善。然而一个较小型的框架含有你所需要(或者以后需要)的各种东西,它将只能在受更多限制的环境中运行,并且需要学习更小的、更简单的子集学习。
    • 是否选择一个鼓励良好开发实例的框架:比如,一个鼓励 Model-View-Controller 结构来将代码分离到逻辑函数上的框架将会是更加易于维护的代码,想比与那些对开发者没有此期待的框架而言。同样的,框架设计也深刻影响了测试和重复使用代码的难易程度。
  • 框架/编程语言的表现: 通常来讲,“速度”并不是选择中最重要的因素,甚至,相对而言,运行速度很缓慢的Python对于一个在中等硬盘上跑的中等大小的网站已经足够了。其他语言(C++/Javascript)的明显的速度优势很有可能被学习和维护的代价给抵消了。
  • 缓存支持:当你的网站之间变得越来越成功之后,你可能会发现它已经无法妥善处理它收到的大量请求了。在这个时候,你可能会开始考虑添加缓存支持。缓存是一种优化,是指你将全部的或者大部分的网站请求保存起来,那么在后继请求中就不需要重新计算了。返回一个缓存请求比重新计算一次要快得多。缓存可以被植入你的代码里面,或者是服务器中(参见reverse proxy)。web框架对于定义可缓存内容有着不同程度的支持。
  • 可扩展性:一旦你的网站非常成功的时候,你会发现缓存的好处已经所剩无几了,甚至垂直容量到达了极限(将程序运行在更加有力的硬件上面)。在这个时候,你可能需要水平扩展(将你的网站分散到好几个服务器和数据库上来卸载)或者“地理上地”扩展, 因为你的一些客户距离你的服务器很远。你所选择的框架将会影响到扩展你的网站的难易程度。
  • 网络安全:一些网站对于解决常见的网络攻击提供更好的支持。例如,Django消除所有用户从HTML输入的东西。因此从用户端输入的Javascript不会被运行。其他框架也提供相似的功能,但是通常在默认情况下是不直接开启的。

可能还有其他一些原因,包括许可证、框架是否处于动态发展过程中等等。

如果你是一个完全的初学者,那么你可能会给予“易于学习”来选择你的框架。除了语言本身的“易于学习”之外,帮助新手的高质量的文档/教程和一个活跃的社区是你最有价值的资源。在后续课程中,我们选取了Djnago(Python)和Express(Node/Javascript)来编写我们的实例,主要因为它们很容易上手并且有强大的支持。

注意: 我们可以去 Django (Python) 和 Express (Node/JavaScript) 的主页上去看看它们的文档和社区。

  1. 导航至主页 (上面已给出链接)
    • 点击文档菜单的链接 (通常都叫做 "Documentation(文档), Guide(指南), API Reference(API参考), Getting Started(快速开始)"之类的。)
    • 你能看到如何设置URL路由、模板、数据库/数据模型的主题吗?
    • 文档说得够清楚吗?
  2. 导航至各个站点的邮件列表(从社区的链接访问)
    • 近几天提出了多少问题?
    • 有多少问题得到了回应?
    • 他们是否有一个活跃的社区?

几个还不错的框架?

我们继续,来讨论几个特定的服务器端框架。

下面的服务器端框架体现了现在最受欢迎的几个。它们有你需要用来提升效率的一切东西——他们是开源的,一直保持发展的态势,有着富有激情的社区,社区里的人创作出文档并且在讨论板上帮助使用者,并且被使用在很多高质量的网站上。当然还有很多其他非常棒的框架,你可以使用搜索引擎探索一下。

注意:(部分)解释来自框架的官方网站!

Django (Python)

Django是一个高水平的python web框架,它鼓励快速的开发和简洁、务实的设计。它由非常有经验的开发者创建的,考虑到了web开发中会遇到的大多数难题,所以你无需重复造轮就能够专心编写你的应用。 

Django遵循“Batteries included”哲学,并且提供了几乎所有大多开发者们想要“开箱即用”的东西。因为它已经包含了所有东西,它作为一个整体一起工作,遵循着一致的设计原则,并且有扩展的、持续更新的文档。它也是非常快、安全和易于扩展的。基于python,Django代码非常容易阅读和维护。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, unit testing, 和 RESTful request dispatching。它有很好的文档和一个活跃的社区。

Flask已经非常火爆了,部分因为那些需要在小型的、资源受限的系统中提供web服务的开发者们。(比如,在Raspberry Pi, Drone controllers等上面运行服务器)。

Express (Node.js/JavaScript)

Express 针对 Node.js 的快速的、unopinioned、灵活的、小型的web框架(node是用来运行Javascript的无浏览器的环境)。它为web和移动应用提供强大的系列功能,并且传输有用的HTTP工具、方法和middleware.

Express非常受欢迎,主要因为它减轻了客户端Javascript程序到服务器端开发的迁移,并且部分因为它是资源节约型(底层的node环境在单线程使用轻量级多重人物,而不是为每个web请求提供单独的进程)。

因为Express是一个小型的web框架,它几乎不包含任何你可能想要使用的组件(比如,数据库接口和对用户和回话的支持可以通过独立的包来完成)。有很多独立的、非常好的组件,但是有时候你可能很难决定对于特定目的而言哪一个是最好的! 

 很多非常受欢迎的服务器端编程和全栈框架(同时包括服务器端和客户端框架),包括 FeathersItemsAPIKeystoneJSKrakenLEAN-STACKLoopBackMEAN, 和 Sails.

大量的profile company使用Express,包括优步、Accenture、IBM等(这里是一张列表).

Ruby on Rails (Ruby)

Rails (通常被称作"Ruby on Rails")是一个为Ruby语言编写的web框架。

Rails遵循了和Django非常相似的设计哲学。正如Django一样,它提供了检索URLs的标准机制、从数据库中访问数据、从模板中生成HTML页面、格式化数据JSON 或者 XML。同样的,它也鼓励如 DRY (不要重复你自己)的设计模板——尽可能地只写一次代码、MVC(模板-视图-控制中心)以及很多其他的一些。

当然,还有很多由于因为具体设计决定和语言的特性导致的差异。

Rails被用在很多站点中,包括: BasecampGitHub,ShopifyAirbnbTwitchSoundCloud,HuluZendeskSquareHi

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、Stack Overflow等采用。

Mojolicious (Perl)

 

Mojolicious是为Perl语言设计的新一代Web框架。 

在Web的早期阶段,许多人都为了一个叫做 CGI 的优秀的Perl库而学过Perl。它简单到即使你不是太懂这门语言也可以开始使用,而且也强大到足以让你可以用下去。Mojolicious通过最新的技术实现了这个想法。

Mojolicious提供的一些功能是:

  • 实时Web框架,可轻松将单个文件原型,生成为结构良好的MVC Web应用程序;
  • RESTful路由,插件,命令,Perl-ish模板,内容协商,会话管理,表单验证,测试框架,静态文件服务器,CGI /PSGI 检测,一流的Unicode支持;
  • 全栈式 HTTP 和 WebSocket 客户机/服务器架構,由以下技术支持与实作-IPv6,TLS,SNI,IDNA,HTTP / SOCKS5 代理,UNIX 域套接字,Comet(长轮询),保持活动,连接池,超时,cookie,multipart,支持 gzip 压缩
  • 具有CSS选择器支持的 JSON 和 HTML / XML 解析器和生成器;
  • 非常干净,可移植且面向对象的纯 Perl API,没有任何隐藏的魔法;
  • 全新的代码基于多年的经验,免费和开源。

总结

这篇文章展示了web框架如何使得编写和维护服务器端代码变得简单。它也提供了对于几个流行的框架的评价,还讨论了选择一个web框架的标准。你现在至少应该了解了如何为你的服务器端开发选择一个web框架。如果还没有,也不要担心——接下来我们给你一个详细的Django和Express教程,从而让你有一些使用web框架的实战经验。

这个模块的下一章节我们会稍微转变一下思路,我们会讨论一下网络安全。

文档标签和贡献者

最后编辑者: zhuzhangliang,