2.1 初始化

所有Flaks程序都必须创建一个程序实例。

Web服务器使用一种名为Web服务器网关接口(Web Server Gateway Interface,WSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。

程序实例是Flask类的对象,经常使用下述代码创建:

 from flask import Flask
app = Flask(__name__)

Flask类的构造函数只有一个必须指定的参数,即程序注模块或包的名字。

在大多数程序中,Python的 __name__ 变量就是所需的值。

注:将构造函数的name参数传给Flask程序,Flask用这个参数决定程序的根目录,以便稍后能够找到相对于程序根目录的资源文件位置。

后面会介绍更复杂的程序初始化方式,对于简单的程序来说,上面的代码足够了。

2.2 路由和视图函数

客户端(例如Web浏览器)把请求发送给Web服务器,Web服务器再把请求发送给Flask程序实例。

程序实例需要知道对每个URL请求运行哪些代码,所以保存了一个URL到Python函数的映射关系。处理URL和函数之间关系的程序成为路由

在Flask程序中定义路由的最简便方式,是使用程序实例提供的app.route 修饰器,把修饰的函数注册为路由。

下面的例子说明了如何使用这个修饰器生命路由:

 @app.route('/')
def index():
return '<h1>Hello World!</h1>'

注:修饰器是Python语言的标准特性,可以使用不同的方式修改函数的行为。惯常用法是使用修饰器把函数注册为事件的处理程序。

前例把 index() 函数注册为程序跟地址的处理程序。

如果部署程序的服务器域名为 www.example.com  ,在浏览器中访问 http://www.example.com 后,会触发服务器执行 index() 函数。

这个函数的返回值称为 响应,是客户端接收到的内容。如果客户端是Web浏览器,响应就是显示给用户常看的文档。

像 index() 这样的函数称为 视图函数(view function)。

视图函数返回的响应可以是包含HTML的简单字符串,也可以是复杂的表单。

注:在Python代码中嵌入响应字符串会导致代码难以维护,此处这么做只是为了介绍响应的概念。

如果你仔细观察日常所用服务的某些URL格式,会发现很多地址中都包含可变部分。

例如,你的Facebook资料页面的地址是 http://facebook.com/<your-name>,用户名<your-name>是地址的一部分。

Flask支持这种形式的URL,只需要在route修饰器中使用特殊的句法即可。

下面定义的路由中就有一部分是动态名字:

 @app.route('/user/<name>')
def user(name);
return '<h1>Hello,%s!</h1>' % name

尖括号中的内容就是动态部分,任何能匹配静态部分的URL都会映射到这个路由上。

调用视图函数是,Flask会将动态部分作为参数传入函数。

在这个视图函数中,参数用于生成针对个人的欢迎消息。

路由中的动态部分默认使用字符串,不过也可是使用类型定义。

例如,路由  /user/<int:id> 只会匹配动态片段 id 为整数的URL。

Flask 支持在路由中使用 int 、float 和 path 类型。
path类型也是zfc,但不把斜线视作分隔符,而将其当做动态片段的一部分。

2.3 启动服务器

程序实例用 run 方法启动 Flask 集成的开发Web服务器:

 if __name__ == '__main__':
app.run(debug=True)

__name__ == '__main__' 是Python的惯常用法,在这里确保执行这个脚本是才启动开发Web服务器。

如果这个脚本又其他脚本引入,程序假定父级脚本会启动不同的服务器因此不会执行 app.run()

服务器启动后,会进入轮询,等待并处理请求。轮询会一直运行,直到程序停止,比如按 Ctrl-C 键。

有一些选项参数可被 app.run() 函数接受用于设置Web服务器的操作模式。

在开发过程中启动调试模式会带来一些便利,比如激活调试器和重载程序。

要想启用调试模式,我们可以吧debug参数设置为True。

注:Flask提供的Web服务器不适合在生产环境中使用。

2.4 一个完整的程序

前面介绍了Flask Web程序的不同组成部分,现在开发一个程序。

示例2-1  hello.py :一个完整的Flask程序

 from flask import Flask
app = Flask(__name__) @app.route('/')
def index():
return '<h1>Hello World!</h1>' if __name__ == '__main__':
app.run(debug=True)

想要运行这个程序,请确保激活了你之前创建的虚拟环境,并在启动安装了Flask。

然后使用下述命令启动程序: python hello.py

现在打开Web浏览器,在地址栏输入  http://127.0.0.1:5000/ 。

如果输入其他地址,程序将不知道如何处理,因此会向浏览器返回错误代码 404 。访问不存在的网页时,我们疆场看到这个错误。

示例2-2 是这个程序的增强版,添加了一个动态路由。

访问这个地址时,你会看到一则针对个人的欢迎消息。

示例2-2 hello.py:包含动态路由的Flask程序

  from flask import Flask
app = Flask(__name__) @app.route('/')
def index():
return '<h1>Hello World!</h1>' @app.route('/user/<name>')
def user(name):
return '<h1>Hello , %s !</h1>' %name if __name__ == '__main__':
app.run(debug=True)

测试动态路由前,你要确保服务器正在运行中。

然后访问  http://localhost:5000/user/Dava

程序会显示一个使用 name 动态参数生成的欢迎消息。

尝试使用不同的名字,可以看到视图函数总是使用指定的名字生成响应。

2.5 请求-响应循环

为进一步了解Flask的共工作方式。介绍Flask框架的一些设计理念。

2.5.1 程序和请求上下文

Flask从客户端收到请求时,要让视图函数能访问一些对象,这样才能处理请求。

请求对象就是一个很好的例子,它封装了客户端发送的HTTP请求。

要想让视图函数能够访问请求对象,一个显而易见的方式是将其作为参数传入视图函数,不过这会导致程序中每个视图函数都增加一个参数。

除了访问请求对象,如果视图函数在处理请求时还要访问其他对象,情况会变得更糟。

为了避免大量可有可无的参数把视图函数弄得一团糟,Flask使用上下文临时把某些对象变为全局可访问。有了上下文,就可以就可以写出下面的视图函数:

 from flask import request

 @app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your brower is %s</p>' % user_agent

注意:在这个视图函数中我们如何把request当做全局变量使用。事实上,request不可能是全局变量。试想,在多线程服务器中,多个线程同时处理不同客户端发送的不同请求时,每个线程看到的request对象必然不同。Flask使用上下文让特定的变量在一个线程中全局可访问,与此同时却不会干扰其他线程。

注:线程是可单独管理的最小指令集。进程经常使用多个活动线程,有时还会共享内存或文件句柄等资源。多线程Web服务器会创建一个线程池,再从线程池中选择一个线程用于处理接受到的请求。

在Flask中有两种上下文:程序上下文和请求上下文。表2-1列出了这两种上下文提供的变量。

Flask上下文全局变量

变量名 上下文 说明
current_app 程序上下文 当前激活程序的程序实例
g 程序上下文 处理请求时用作临时存储的对象。每次请求都会重设这个变量
request 请求上下文 请求对象,封装了客户端发出的HTTP请求中的内容
session 请求上下文 用户会话,用户存储请求之间需要“记住”的值的词典

Flask在分发请求之前激活(或推送)程序和请求上下文,请求处理完成之后再将其删除。

程序上下文被推送后,就可以在线程中使用current_app和g变量。

请求上下文被推送后,就可以使用request和session变量。
如果使用这些变量时,我们没有激活程序上下文或请求上下文,就会导致错误。

下面这个 Python shell 会话演示了程序上下文的使用方法:

 >>> from hello import app
>>> from flask import current_app
>>> current_app.name
Traceback (most recent call last):
......
RuntimeError: Working outside of application context. >>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> current_app.name
'hello'
>>> app_ctx.pop()
>>>

在这个例子中,没激活程序上下文之前就调用 current_app.name 会导致错误,但是推送玩上下文之后就可以调用了。

注意,在程序实例上调用 app.app_context() 可获得一个程序上下文。

2.5.2 请求调度

程序收到客户端发来的请求时,要找到处理该请求的视图函数。为了完成这个任务,Flask会在程序的URL映射中查找请求的URL。

URL映射是URL和视图函数之间的对应关系。

Flask使用  app.routwe()  修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。

要查看Flask程序中的URL映射是什么样子,我们可以在 Python shell 中检查hello.py生成的映射。

测试之前,确保激活了你的虚拟环境:

 >>> from hello import app
>>> app.url_map
Map([<Rule '/' (OPTIONS, GET, HEAD) -> index>,
<Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>,
<Rule '/user/<name>' (OPTIONS, GET, HEAD) -> user>])
>>>

/ 和 /user/<name> 路由在程序中使用 app.route 修饰器定义。

/static/<filename> 路由是Flask 添加的特殊路由,用于访问静态文件。后面会介绍静态文件。

URL映射中的HEAD、OPEIONS、GET是请求方法,有路由进行处理。

Flask 为每个路由都指定了请求方法,这样不同的请求方法发送到相同的URL上时,会使用不同的视图函数进行处理。

HEAD 和 OPTIONS 方法由Flask自动处理,因此可以这么说,在这个程序中,URL映射中的3个路由都使用GET方法。后面会介绍如何为路由指定不同的请求方法。

2.5.3 请求钩子

有时在处理 请求之前或之后执行代码很有用。

例如,在请求开始时,我们可能要创建数据库连接或者认证发起请求的用户。

为了避免在每个视图函数中都使用重复的代码,Flask提供了注册通用函数的功能,注册的函数可在请求被分发到函数之前或之后调用。

请求钩子使用修饰器实现。Flask支持以下4中钩子。

before_first_request :注册一个函数,在处理第一个请求之前运行。

before_request  : 注册一个函数,在每次请求之前运行。

after_request  :注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。

teardown_request  : 注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。

在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g  。

例如: before_request处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视图函数时,视图函数再使用 g.user 获取用户。

2.5.4 响应

Flask 调用视图函数后,会将其返回值作为响应的内容。大多数情况下,响应就是一个简单的字符串,作为HTML页面回送客户端。

但HTTP协议需要的不仅是作为请求响应的字符串。HTTP响应中一个很重要的部分是状态码。

Flask 默认设为 200 ,这个代码表明请求已被成功处理。

如果视图函数返回的响应需要使用不同的状态码,那么可以把数字代码作为第二个返回值,添加到响应文本之后。

例如,下述视图函数返回一个 400 状态码,标示请求无效。

 @app.route('/')
def index():
return '<h1>Bad Request</h1>',400

视图函数返回的响应还可接受第三个参数,这事一个由首部(header)组成的字段,可以添加到HTTP响应中。一般情况下并不需要这么做,不过你会在14章看到一个例子。

如果不想返回由1个、2个或3个值组成的元组,Flask视图函数还可以返回Response对象。

make_response() 函数可以接受1个、2个或3个参数(和视图函数的返回值一样),并返回一个Response对象。

有时我们需要在视图函数中进行这种转换,然后在响应对象上调用各种方法,进一步设置响应。

下例创建了一个响应对象,然后设置了cookie:

 from flask import make_response

 @app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('ansewer','')
return response
from flask import Flask
from flask import make_response
app = Flask(__name__) @app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('ansewer','')
return response if __name__ == '__main__':
app.run(debug=True)

有一种名为重定向的特殊响应类型。

这种响应没有文档页面,只告诉浏览器一个新地址用以加载新页面。

重定向经常在Web表单中使用。

重定向经常使用 302 状态码表示,指向的地址由 Location 首部提供。

重定向可以使用3个值形式的返回值生成,可可在Response对象中设定。

不过由于使用频繁,Flask提供了 redirect() 辅助函数,用于生成这种响应:

 from flask import redirect

 @app.route('/')
def index():
return redirect('http://www.baidu.com')
from flask import Flask
from flask import redirect
app = Flask(__name__) @app.route('/')
def index():
return redirect('http://www.baidu.com') if __name__ == '__main__':
app.run(debug=True)

还有一种特殊的响应有 abort 函数生成,用户处理错误。

在下面这个例子中,如果URL中动态参数 id 对应的用户不存在,就返回状态码 404:

 from flask import abort

 @app.route('/user/<id>')
def get_user(id):
user = load_user(id)
if not user:
abort(404)
return '<h1>Hello,%s</h1>' % user.name

注意:abort 不会把控制权交还给调用他的函数,而是抛出异常把控制权交给Web服务器。

2.6 Flask 扩展

Flask 被设置为可扩张形式,故而没有提供一些重要的功能,例如数据库和用户认证,所以开发者可以自由选择最合适的程序包,或者按需自行开发。

社区成员开发了大量不同用途的扩展,如果还不能满足需求,你还可以使用所有Python标准包或代码库。

为了让你知道如何把扩展整合到程序中,接下来我们将在hello.py中添加一个扩展,使用命令行参数增强程序的功能。

使用Flask-Script支持命令行选项

Flask 的开发Web服务器支持很多启动设置选项,但只能在将本中作为参数传给app.run()函数。

这种方式并不十分方便,传递设置选项的理想方式是使用命令行参数。

Flask-Script是一个Flask扩展,为Flask程序添加了一个命令行解析器。

Flask-Script 自带了一组常用选项,而且还支持自定义命令。

Flask-Script 扩展使用pip安装:

pip install flask-script

示例2-3显示了把命令行解析功能添加到hello.py程序中需要修改的地方。

示例2-3 hello.py : 使用Flask-Script

from flask_script import Manager
manager = Manager(app) #... if __name__ == "__main__":
manager.run()
from flask import Flask,render_template
from flask import request
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app) @app.route("/") #路由
def index(): #视图函数
return render_template('index.html') @app.route("/user/<name>") #动态路由
def user(name):
return render_template('user.html',name=name) if __name__ == "__main__":
manager.run()

这个扩展的初始化方法也适用于其他很多扩展:把程序实例作为参数传给构造函数,初始化主类的实例。

创建的对象可以在各个扩展中使用。

在这里,服务器由 manager.run() 启动,启动后就能解析命令了。

这样修改之后,程序可以使用一组基本命令行选项。

现在运行hello.py,会显示一个用法消息:

(venv) E:\Gitee\flasky>python hello.py
usage: hello.py [-?] {shell,runserver} ... positional arguments:
{shell,runserver}
shell Runs a Python shell inside Flask application context.
runserver Runs the Flask development server i.e. app.run() optional arguments:
-?, --help show this help message and exit

shell命令用于在程序的上下文中启动Python shell会话。你可以使用这个会话中运行维护任务或测试,还可以调试异常。

顾名思义, runserver 命令用来启动Web服务器。

运行 python hello.py runserver 将以调试模式启动Web服务器。

但是我们还有很多选项可用:

(venv) E:\Gitee\flasky>python hello.py runserver --help
usage: hello.py runserver [-?] [-h HOST] [-p PORT] [--threaded]
[--processes PROCESSES] [--passthrough-errors] [-d]
[-D] [-r] [-R] [--ssl-crt SSL_CRT]
[--ssl-key SSL_KEY] Runs the Flask development server i.e. app.run() optional arguments:
-?, --help show this help message and exit
-h HOST, --host HOST
-p PORT, --port PORT
--threaded
--processes PROCESSES
--passthrough-errors
-d, --debug enable the Werkzeug debugger (DO NOT use in production
code)
-D, --no-debug disable the Werkzeug debugger
-r, --reload monitor Python files for changes (not 100% safe for
production use)
-R, --no-reload do not monitor Python files for changes
--ssl-crt SSL_CRT Path to ssl certificate
--ssl-key SSL_KEY Path to ssl key

--host 参数是个很有用的选项,它告诉Web服务器在哪个网络接口上监听来自客户端的连接。

默认情况下,Flask开发Web服务器监听localhost上的连接,所以只接受来自服务器所在计算机发起的连接。

下述命令让Web服务器监听公共网络接口上的连接,允许同网中的其他计算机连接服务器:

python hello.py runserver --host 0.0.0.0

现在,Web服务器可使用 http://a.b.c.d:5000/ 网络中的任意一台电脑访问,其中 “a.b.c.d” 是服务器所在计算机的外网IP地址。

本章介绍了请求响应的概念,不过相应的知识还有很多。对于使用模板生成相应,Flask提供了良好支持。

基于Python的Web应用开发实战——2 程序的基本结构的更多相关文章

  1. 学习参考《Flask Web开发:基于Python的Web应用开发实战(第2版)》中文PDF+源代码

    在学习python Web开发时,我们会选择使用Django.flask等框架. 在学习flask时,推荐学习看看<Flask Web开发:基于Python的Web应用开发实战(第2版)> ...

  2. FlaskWeb开发:基于Python的Web应用开发实战

    所属网站分类: 资源下载 > python电子书 作者:熊猫烧香 链接:http://www.pythonheidong.com/blog/article/63/ 来源:python黑洞网,专注 ...

  3. 基于Python的Web应用开发实战——3 模板

    要想开发出易于维护的程序,关键在于编写形式简洁且结构良好的代码. 当目前为止,你看到的示例都太简单,无法说明这一点,但Flask视图函数的两个完全独立的作用却被融合在了一起,这就产生了一个问题. 视图 ...

  4. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(上)

    目录 前言 第1章 安装 第2章 程序的基本结构 第3章 模板 第4章 Web表单 第5章 数据库 第6章 电子邮件 第7章 大型程序的结构   前言 学习Python也有一个半月时间了,学到现在感觉 ...

  5. 基于Python的Web应用开发实践总结

    基于Python的Web应用开发学习总结 项目地址   本次学习采用的是Flask框架.根据教程开发个人博客系统.博客界面如图所示. 整个学习过程收获很多,以下是学习总结. 1.virtualenv ...

  6. 基于Python的WEB接口开发与自动化测试 pdf(内含书签)

    基于Python的WEB接口开发与自动化测试 目录 目 录O V目 录章 Python 学习必知 ................................................... ...

  7. 基于Python的Flask的开发实战(第二节程序的基本结构)

    1.初始化 所有的flask程序都必须创建一个程序实例 web服务器使用wsgi接口协议,把接收客户端的请求都转发给这个程序实例来进行处理.这个程序实例就是flask对象 from flask imp ...

  8. 基于python的web应用开发-添加关注者

    社交web允许用户之间相互联系. 例如: 关注者.好友.联系人.联络人或伙伴. 记录两个用户之间的定向联系,在数据库查询中也要使用这种联系. 一.论数据库关系 一对多关系 数据库使用关系建立记录之间的 ...

  9. 基于Python的Flask的开发实战(第一节Flask安装)

    1.安装python虚拟环境 easy_install virtualenv easy_install pip cd /home/admin virtualenv flask-website sour ...

随机推荐

  1. 01 mybatis框架整体概况(2018.7.10)-

    01 mybatis框架整体概况(2018.7.10)- F:\廖雪峰 JavaEE 企业级分布式高级架构师课程\廖雪峰JavaEE一期\第一课(2018.7.10) maven用的是3.39的版本 ...

  2. Identity Server 4 原理和实战(完结)_建立Identity Server 4项目,Client Credentials 授权实例

    创建项目 dotnet new -i IdentityServer4.Templates 多出来的这些模板 adminUI用来测试,想要用再生产环境,需要交钱 结合core的 Identity来使用 ...

  3. linux的grep命令参数全拼详解

    今天为了查找文件中某段字符,找了好久,最后成功使用指令: find . -name "*.cpp" |xargs grep -in “get_itemInfo” | grep -v ...

  4. win7 win8 快捷键直接调出任务管理器

    问:windos7如何设置按ctrl+alt +delete三个键就直接出现任务管理器. 答:你应该这样按Ctrl+shift+esc,这样就可以直接调出任务管理器,而且一只手就可以完成,大拇指按Ct ...

  5. window.showModalDialog 在谷歌Uncaught TypeError: undefined is not a function

    if(navigator.userAgent.indexOf("Chrome") >0 ){var winOption = "height="+heigh ...

  6. C# web 总结

    (1)Cshtml 中 “@” 符号转义 在 cshtml 中需要使用 “@” 符号,如 “@幸福摩天轮版权所有”.那么我们需要使用转义,使用 “@@” 就好!“© ”和 “@” 好像呀. <t ...

  7. C/C++内存检测工具Valgrind

    内存检测Valgrind简介 Valgrind是运行在Linux上一套基于仿真技术的程序调试和分析工具,作者是获得过Google-O'Reilly开源大奖的Julian Seward, 它包含一个内核 ...

  8. 計蒜客/小教官(xjb)

    題目鏈接:https://nanti.jisuanke.com/t/366 題意:中文題誒~ 思路: 先通過給出的條件構造一個符合題意的數組(可以是任意一個符合條件的數組,菜雞不會證明: 然後構造的數 ...

  9. CSS之html元素与body元素的范围

  10. 黑马方法引用学习 Stream流 函数式接口 Lambda表达式 方法引用