Web编程是程序设计应用之一,随着动态网站不断发展,Web编程已经成为程序设计的重要应用领域。目前Web编程主要有ASP.NET、PHP、Java等编程语言,Python语言也可以像其他语言一样应用于Web服务。
17.1 Web网站编程概述
Web是一个由许多互相链接的超文本组成的系统,通过互联网访问。这些超文本内容通过超文本传输协议(Hypertext
TransferProtocol)传送给用户,而后者通过点击链接来获得相关内容。其基本过程如图17.1所示。
客户端使用浏览器(Browse)通过互联网和HTTP协议向服务器发出请求,服务器收到请求后,如果包含相关资源,则会以HTTP协议向客户端回应其请求的相关内容。这种工作模式通常被简称为B/S(Browse/Server)模式,也是目前流行的软件工作模式之一。
Web编程就是一般分为前端和后端编程,前端编程就是使用HTML语言写出网页框架及有关内容,使用Javascript写在客户端运行的与用户或服务器端交互的程序。后端编程的主要任务是完成网站与客户端相关信息的交换与处理,也包括将数据存入数据库和从数据库中查询出数据。
本书所介绍的Web编程主要是指Web后端编程,即通过服务器端程序实现业务逻辑,完成数据处理的相关功能,比如接受用户数据、对用户数据进行校验并保存至数据库中,再如从数据库中查询出相关数据,用来校验或将数据发送给用户。
17.2 Flask Web框架及其应用
Flask是一个使用Python编写的轻量级Web应用框架,它的安装依赖Werkzeug WSGI工具箱和Jinja2模板引擎,并且使用BSD授权。
17.2.1 Flask Web框架简介
Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其他功能。Flask没有默认使用的数据库、窗体验证工具。然而,Flask保留了扩展的弹性,可以用Flask-extension加入以下功能:ORM、窗体验证工具、文件上传、各种开放式身份验证技术等。
作为Web框架的Flask,其主要特点如下:
核心简单而且可扩展;
支持secure cookies(client side sessions);
Unicode based;
内置开发用服务器和debugger;
集成单元测试(unit testing);
100% WSGI 1.0兼容;
使用Jinja2模板引擎;
可用Extensions增加其他功能。
总之,Flask可以变成你任何想要的东西,一切恰到好处,由你做主。Flask通过扩展为你的应用添加这些功能,就如同这些功能是Flask原生的一样。Flask可能是“微小”的,但它已经为满足您的各种生产需要做出了充足的准备。
17.2.2 Flask Web框架初识
Flask框架并不是Python语言的标准库,所以要使用它,必须先进行安装。如果你可以使用pip命令来安装最为简单,因为它会自动帮你安装其依赖的第三方库,在命令提示符下使用以下安装命令:
pip install flask
安装完成后,在交互式环境下使用import Flask语句,如果没有错误提示,则说明安装成功。
如果你要通过下载手动安装,必须先下载安装Flask依赖的两个外部库,即Werkzeug和Jinja2,分别解压后进入对应的目录,在命令提示符下使用python setup.py install来安装它们。它们的下载地址分别为:
https://github.com/mitsuhiko/jinja2/archive/master.zip
https://github.com/mitsuhiko/werkzeug/archive/master.zip
然后再下载并用相同的命令来安装Flask,其下载地址为:
http://pypi.python.org/packages/source/F/Flask/Flask-0.10.1 .tar.gz
就像学习一门编程语言一样,要了解Flask框架,最好的方法是首先了解其基本应用的建立方法。下面通过一个最简单的Web实例来说明Flask框架的基本使用方式。
如果要验证这个Web服务程序是正常运行的,可以打开浏览器,在地址栏中输入如图17.2所示的网址,就可以访问这个最简单的服务器了。如图17.3所示,访问得到的页面内容为“你好,我是Flask!”,这正是本例代码中的函数helo()返回的字符串。
注意: 此处是直接在IDLE中运行的,如果要更好地调试,请在命令提示符下使用“python filename.py”来运行和调试服务器。
17.2.3 URL装饰器与URL参数传递
通过实例17-1代码可以看出,URL装饰器是Flask实例的route()方法,它可以实现将一个普通函数与特定的URL关联起来,让服务器收到这个URL请求时将调用这个函数以返回相应内容。一个函数可以由多个URL装饰器来装饰,实现多个URL请求由一个函数产生的内容回应。对上一节实例稍加修改就可以实现将不同的URL映射到同一个函数上。
注意: GET请求一般是直接在浏览器地址栏中输入URL或单击链接产生的请求方式;POST请求一般由提交表单时根据指定的请求方法POST,而产生的对Web服务器的请求。
【代码说明】代码中预先定义了GET请求要返回的页面内容字符串html_txt,在业务函数helo()的装饰器中提供了methods参数为“GET”、“POST”字符串列表,表示URL为'/hello'的请求,不论是‘GET’还是‘POST’方法,都映射到该函数。在业务函数helo()内部使用flask.request.method来判断收到的请求方法是“GET”还是“POST”方法,然后分别返回不同的内容。
一般情况下,通过浏览器来传参数相关数据或参数都是通过GET或POST请求中包含参数来实现的。实际上通过URL也是可以传递参数的。通过URL传递参数的方法是直接将数据放入URL中,然后在服务器端进行获取。
Flask中获取URL参数要在URL装饰器和业务函数中分别进行定义或处理,URL变量规则有以下两种形式(URL装饰器中的URL字符串写法):
【代码说明】代码中使用了带URL参数的URL装饰器,会将匹配的参数赋予name变量,在业务函数helo()中对应地列出变量名name,用来接收参数值,在业务函数中就可以使用传来的值了。
【运行效果】如图17.9所示,URL请求中包含了参数“李明”,在服务器端的业务函数中引用它并返回给浏览器请求内容中也包含了这个参数值。
17.2.4 通过URL传递参数GET与POST请求的参数传递
实际上,客户端与服务器传递参数的主要方式还是使用GET或POST参数传递,Flask也提供了简单的参数获取方法。
获取GET请求参数的基本方式是调用flask.request.args对象的get()方法,形式如下:
flask.request.args.get (name) #name为参数名称
如果是POST请求的参数,需要使用flask.request的form字典对象来获取,调用形式如下:
flask.request.form[‘name’] #name为参数名称
但用这种方法获取的参数名不存在时会导致400错误请求页面。因此如果要使用这种方法来获取参数时,可以使用短if语句或逻辑运算形式:
注意: 这种方式也能获取GET请求参数。
【代码说明】代码和实例17-3代码是相似的。在页面代码字符串中添加输入姓名的表单,在业务函数hello()中,添加了获取POST参数的语句并根据用户的输入返回页面的内容。另外,此处的主程序run()函数中使用了参数debug=True,这样,每次修改保存代码后,服务器会自动重新启动,并且当业务函数引发错误时,会在页面中显示debug信息用来调试。
【运行效果】GET请求的页面如图17.10所示,当用户输入“王伟”时,POST请求的页面内容如图17.11所示,当用户没有输入内容时,POST请求的页面内容如图17.12所示。
17.2.5 使用cookie与session跟踪客户
HTTP协议是一种无状态的协议,当服务器收到客户端请求时,无法知晓请求的客户端是谁。因此,产生了cookie与session用来存储交互状态。cookie是运用客户端存储交互状态的一种方式,而session则主要是在服务器端存储交互状态的一种方式。
Flask框架也提供了这两种常用的交互状态存储方式,但是session与有些框架是不同的。Flask中的session是以密钥签名加密的cookie,即用户可以查看你的cookie,但是如果没有密钥就无法修改它,而且也是保存在客户端的。
获取cookie可以使用:
flask.request.cookies.get(‘name’)
设置cookie则需要使用make_response对象:
【实例17-6】 演示了一个使用cookie跟踪用户的实例,代码如下:
【实例17-7】 演示了一个使用session跟踪用户的实例,代码如下:
17.2.6 使用静态文件资源与页面文件
网站中的网页必然要用到图片、CSS、js等文件,而Flask框架就已经为你准备好了。要使用这种静态文件,只要在模板文件中使用以下语句:
url_for(‘static’, filename=‘my.jpg’)
它会生成一个网址/static/my.jpg,要求静态文件保存在当前目录的static文件夹下。
另一方面,前文介绍的服务器的页面内容都是以字符串的形式保存在服务器程序中的。这种使用方法肯定是不好的,既不利于维护,也不利于程序员分工。其实,你可以使用flask.render_template()来调用当前目录下的templates子目录中的HTML文件,其具体形式为:
flask.render_template(‘name.html’,name=‘name’)
其中参数:
name.html是要返回页面内容的文件名;
name传递变量name的值为字符串’name’,供其在页面文件内容中输出相关信息。
【实例17-8】 演示了使用独立HTML文件的URL服务,并在其中引用了服务器中的图片文件,代码如下:
注意: 此处使用了jinjia2的模板渲染库,它已经在安装Flask时作为依赖库被安装了。具体的使用方法,你可以参考相关资料。
【代码说明】程序代码还是很简单的,只不过在业务函数中返回了flask.render_template()方法的结果,来渲染index.html页面文件。index.html文件中使用了url_for(‘static’,filename=‘test.jpg’)来生成静态资源图片文件test.jpg的链接。
【运行效果】如图17.18所示,经过渲染的页面中显示了准备的图片。
17.2.7 接收上传文件
使用Flask框架编写上传文件的服务器端也很简单,它与处理GET或POST参数具有相似的地方。客户端上传的文件相关信息会被保存在flask.request.files对象中,通过这个对象,可以获取上传来的文件名和文件对象,然后调用文件对象的save()来将文件保存到指定的目录中即可。
【实例17-9】 演示了一个文件上传的基本例子,代码如下:
注意: 本上传文件实例在实际应用时应添加一些校验文件类型及文件名称的功能。
17.2.8 Flask框架中使用数据库
在上一章中介绍了数据库的相关操作,在网站开发中就是要大量使用数据库系统的;数据库系统是网站系统的主要构成部分之一,它主要用来保存网站的相关信息和用户的有关信息。所以在网站开始中要经常在业务方法中访问数据库。而访问数据库一般都建立连接、查询数据、关闭连接。
在Flask框架中提供了一种只需要定义一次连接和定义一次释放连接,之后就可以在所有的业务函数中直接使用数据连接来查询数据库。这就需要使用Flask框架中的两个装饰器和一个g对象。
两个装饰器分别是before_request()和teardown_request(),被before_request()装饰的函数会在每个请求之前调用,而被teardown_request()装饰的函数会在每个请求结束之后调用。此外,还有一个after_request()装饰器,只不过它在业务函数中引发错误时它装饰的函数不会被执行。
Flask提供了特殊的g对象,这个对象与每一个请求是一一对应的,并且只在函数内部有效。不要在其他对象中存储类似信息,因为在多线程环境下无效。这个特殊的g对象会在后台神奇地工作,保证系统正常运行。
将刚才介绍的装饰器和g对象结合起来,就可以实现在所有的请求处理方法中直接使用数据库,而不用在请求处理方法中去重复地编写数据库连接与断开的代码。其基本使用代码如下:
注意: 使用这种装饰器只要在项目中定义一次,可在整个网站程序中直接应用,而不用多次定义。
【实例17-10】 演示了一个使用数据库的保存用户注册信息和验证登录的简单实例,各请求方法的主要代码如下,详细代码请参阅本书所附源代码C17目录中的a17_10.py:
根据网站需求,还定义了两个模板文件,它们分别为login.html和signup.html,其基本代码如下:
【代码说明】本实例网站服务器代码总共不超过80行,但是完成了一个网站项目所需要的基本功能,即网站主页、注册页面与注册功能、登录页面与登录功能、注销功能。共定义了3个数据库相关操作的函数和4个网站服务器业务函数(index()、signup()、login()、logouot())。
注意:以上网站项目代码在实际应用中会将业务函数中的一些功能进行分割,把实现不同的功能模块划分到不同的模块中,以便于程序的维护和项目的分工。
17.3 Tornado Web框架及其应用
Tornado也是一种目前比较流行的、强大的、可扩展的Python的Web非阻塞式开源服务器框架,也是一个异步的网络库。让你能够快速简单地编写高速的Web应用。本节主要介绍Tornado框架的应用。
17.3.1 Tornado框架简介
Tornado是基于Bret Taylor和其他人员为FriendFeed所开发的网络服务框架,当FriendFeed被Facebook收购后得以开源。Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个拥有非常高性能的框架。此外,它还拥有处理安全性、用户验证、社交网络以及与外部服务(如数据库和网站API)进行异步交互的工具。
自2009年发布以来,Tornado已经获得了很多社区的支持,并且在一系列不同的场合得到应用。除FriendFeed和Facebook外,还有很多公司在生产上转向Tornado,包括Quora、Turntable.fm、Bit.ly、Hipmunk以及MyYearbook等。
Tornado库可以大体上分为四个部分:
17.3.2 Tornado框架初识
要使用Tornado这个Web框架,也必须先安装它。其安装方法与本书前面所述的各种第三方库的安装方法相同。
pip install tornado
到https://pypi.python.org/packages/source/t/tornado/tornado-4.1.tar.gz网址下载tornado的源码,解压缩后,在命令提示符下对该子目录执行以下命令:
python setup.py install
Tornado框架来编写Web服务器端程序就是通过继承tornado.Web.RequestHandler类,并编写get()、post()业务方法,以实现对客户端指定URL的GET请求和POST请求的回应。然后启动框架中提供的服务器以等待客户端连接、处理相关数据并返回请求信息。
用Tornado框架编写Web服务器的基本代码框架如下:
通过以上框架可以看出,用Tornado框架编写服务器端程序的代码结构是非常清晰的。其基本工作就是编写相关的业务处理类,并将它们和某一特定的URL映射起来,Tornado框架服务器收到对应的请求后进行调用。
一般来说简单的网站项目可以把所有的代码放入同一个模块之中,但为了维护方便,可按照功能将其划分到不同的模块中,其一般模块结构(目录结构)如下:
【实例17-11】 演示了一个使用Tornado框架编写的最基本服务器程序,代码如下:
【代码说明】由以上代码可以看出,用Tornado框架写一个基本的Web服务器端程序也不过十行代码且代码结构清晰。首先要导入Tornado相关模块,然后自定义URL的响应业务方法(GET、POST等),其次是实例化Tornado模块中提供的Application类,并传URL映射列表及有关参数,最后启动服务器即可。在命令提示符下的对应子目录中执行:
python a17_11.py
如果没有语法错误的话,服务器就已经启动并等待客户端连接了。
17.3.3 请求参数的获取
与17.2节所述,客户端请求的参数有URL中的参数、GET请求中的参数和POST请求中的参数。
在Tornado框架中,要获取URL中包含的参数,与Flask框架有相似之处,它们都是在URL定义中定义获取参数,并在对应的业务方法中给出相应的参数名进行获取。
Tornado框架URL定义字符串中,使用正则表达式来匹配URL及URL中的参数,比如:
(r"uid/([0-9]+)",UserHdl)
这种形式的URL字符串定义可以接受形如“uid/”后跟一位或多位数字的客户端URL请求。
对应以上URL定义,可以以如下方式定义get()方法:
【实例17-12】 演示了在GET方法中获取URL中参数的基本实例,代码如下:
【实例17-13】 演示了一个获取POST请求中参数的实例,代码如下:
17.3.4 用cookie与安全cookie跟踪客户
Tornado框架提供了直接操纵cookie和安全cookie的方法。安全的cookie就是存储在客户端的cookie是经过加密的,客户端只能查看到加密后的数据。使用cookie和安全cookie的基本原型方法如下:
注意: 要使用安全cookie,必须为Application类提供cookie_secret参数,以给出加密的密钥。
【实例17-14】 演示了一个在不同页面设置与获取cookie值的实例,代码如下:
注意: 因字符串编码的问题,在设置cookie字符串中有中文字符时,要使用tornado.escape模块中的URL编码与解码,否则会出现乱码的现象。
此外,Tornado框架中并不提供session功能,你要使用就必须自己实现功能。
17.3.5 URL转向与静态文件资源
在Tornado框架的Web编程中,也可以实现与Flask中相同的URL转向的功能。Tornado框架中有两种方法可以实现URL转向:
【实例17-15】 演示了两种URL转向的实例,代码如下:
【实例17-16】 演示了在网站页面中引用图片的实例,代码如下:
注意: 此处的静态资源目录是相对于运行时所在的路径目录,所以运行服务器命令行时应进入对应的目录,否则无法找到静态资源。你可以使用绝对路径来排除此问题。
17.3.6 Tornado Web框架应用举例
从上面对Tornado框架介绍中可以看出,使用它开发一个网站项目还是比较容易和快速的。本节以开发一个基本的互动学习系统为例,演示和讲解用Tornado框架开发网站的基本过程。
这个被称为智动交互式学习系统只有两个主要页面,其中主页面用来登录、学习以及管理员管理(如图17.33所示),还有一个用于用户注册的页面。它实现的主要功能是教师先在主页面添加学习主题,即某节课学习的相关问题并提交(问题直接为文本形式),如图17.34所示。当学生未登录可以显示教师发布的学习主题以及每个主题的学习问题,如果学生登录,则可以回答问题。同时,还可以在主页中进行互动问题的讨论,如图17.35所示。
数据库操作的相关功能模块被放在同一个文件(模块)中,详细代码请看本书所附源代码C17目录中的stir-mentality子目录下的db_mgr.py源代码文件。首先自定义一个带上下文管理功能的数据库操作类(实现了上下文管理器的协议方法_enter_()和_exit_()),以方便在实际数据库操作中使用,其代码如下:
其次定义一个数据库初始化的函数,供网站程序启动时调用,其主要功能就是创建前文所述的数据库表,代码如下:
最后,为每个数据库表定义了一个操作数据库的类,即数据业务方法。此处列出学生的用户表(studs)的业务方法类(其他数据库表的操作类请参阅本书所附源文件的C17目录中stir-mentality子目录下的db_mgr.py源代码文件),其中定义了初始化类的构造方法、保存用户名等数据到数据库的save()方法、验证用户名及密码检测登录的isRgstr()方法、查询己注册过的用户等方法,这样在定义响应用户的URL请求的方法中,就可以直接实例化此类,并调用其数据库业务方法,而不用将代码全部写入URL请求方法中,以达到分离代码的目的,方便代码的维护。其代码如下:
有了这些数据处理类,服务器的主程序就得到大量简化,当程序需要操作或查询数据库中的数据时,直接调用这个模块中的相关函数或类实例的方法即可。
该学习系统的主程序代码请参阅本书附源文件的C17目录中stir-mentality子目录下的manger.py源代码文件。
主程序代码中首先自定义了继承tornado.Web.RequestHandler类的BaseHandler类,并通过重载其render()方法替换其模板引擎为mako,其代码如下:
其后的大部分代码都是定义映射到指定URL的处理器类以响应浏览器客户端的请求,通过调用数据库业务方法,实现数据的持久化和从数据库中查询获取数据。通过把数据库的相关操作移至一个功能模块,主程序的代码得以大量简化而且结构清晰。此外,其他的如JavaScript、CSS以及模板文件,请参阅随书资料。
17.4 小结
本章首先介绍了Web开发的基本原理,之后分别介绍了目前流行的Python Web框架Flask和Tornado。详细介绍了两个框架的基本结构和使用方式,并在最后给出了使用Tornado框架编写的一个智动交互式学习系统小型网站项目。通过本章学习,你应掌握Web工作基本原理、常见Web开发的基本框架和开发方式。