文章目录
两个核心依赖
falsk主要依赖两个库 —— Werkzeug 和 Jinja。
Jinja2
由于大多数Web程序都需要渲染模板,与Jinja2集成可以减少大量的工作。此处不展开讨论。
Werkzeug
Flask的核心扩展就是Werkzeug。
python Web框架都需要处理WSGI交互,它是为了让Web服务器与python程序能够进行数据交流而定义的一套接口标准/规范。而Werkzeug是一个优秀的WSGI工具库。
HTTP请求 -》 WSGI规定的数据格式 -》 Web程序
从路由处理,到请求解析,再到响应封装,以及上下文和各种数据结构都离不开Werkzeug。
WSGI程序
根据WSGI的规定,Web程序(WSGI程序)必须是一个可调用对象。这个可调用对象接收两个参数:
- environ:包含了请求的所有信息的字典。
- start_response:需要在可调用对象中调用的函数,用来发起响应,参数是状态码,响应头部等
WSGI服务器会在调用这个可调用对象时传入这两个参数。另外这个可调用对象还要返回一个可迭代对象。
这个可调用对象可以是函数、方法、类或是实现了__call__方法的类实例。
以下借助简单的实例来了解最主要的两种实现:函数和类
# 函数实现
# 可调用对象 接收两个参数
def hello(environ, start_response):
# 响应信息
status = '200 OK'
response_headers = [('Content-type', 'text/html')]
# 需要在可调用函数中调用的函数
start_response(status, response_headers)
# 返回可迭代对象
return [b'<h1>Hello</h1>']
注:WSGI规定请求和响应主体应该为字符串(bytestrings),即py2中的str。在py3中字符串默认为unicode类型,因此需要在字符串前添加b声明为bytes类型,兼容两者
# 类实现
class AppClass:
def __init__(self, environ, start_response):
self.environ = environ
self.statr = start_response
# iter方法,这个类被迭代时,调用这个方法
# 实现该方法的类就是迭代器
def __iter__(self):
status = '200 OK'
response_headers = [('Content-type', 'text/html')]
self.start(status, response_headers)
yield b'<h1>Hello</h1>'
werkzeug中如何实现Web程序
由于flask是基于werkzeug实现的,所以先了解以下werkzeug是如何实现一个简单的web程序
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('hello')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 5000, hello)
通过以上代码,使用run_simple规定了ip、端口号、调用对象
路由是怎么设定的?
Werkzeug怎么实现路由系统
# 路由表
m = Map()
rule1 = Rule('/', endpoint='index')
rule2 = Rule('/downloads/', endpoint='downloads/index')
m.add(rule1)
m.add(rule2)
Flask的路由系统
Flask使用中的路由系统,是通过route() 装饰器来将视图函数注册为路由。进入route函数
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
可见内部调用了add_url_rule,并将函数作为参数传入。看到add_url_rule存在关键的语句
# url_map实际上就是Map类的实例
# rule就是通过route相关更正成的Rule实例
self.url_map.add(rule)
# view_functions是一个字典,存储了端点和视图函数的映射关系。可用于查询
self.view_functions[endpoint] = view_func
再进入底层就会发现,实际上就同上例的werkzeug实现
导入config配置参数
最初,我们修改配置文件会使用以下方法
app.config['DEGUB'] = True
导入参数
import config
app.config.from_object(config)
# 在config.py 文件中 存放配置参数
DEBUG = True
SECRET_KEY = os.urandom(24)
DIALECT = 'mysql'
DRIVER = 'mysqlconnector'
USERNAME = 'root'
PASSWORD = 'root'
HOST = '127.0.0.1'
PORT = '3306'
DATABASE = 'test'
如果自定义了配置文件类也可传入字符串
app.config.from_object('config.Foo')
# 以上代表 config.py文件中的 Foo类
进入from_object() 函数 [位于config.py]
def from_object(self, obj):
if isinstance(obj, string_types):
obj = import_string(obj)
for key in dir(obj):
if key.isupper():
self[key] = getattr(obj, key)
首先判断如果是字符串类型的,做相应处理获得对象。在import_string函数中
module_name, obj_name = import_name.rsplit(".", 1)
module = __import__(module_name, globals(), locals(), [obj_name])
dir()函数的作用:
dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;
带参数时,返回参数的属性、方法列表。
如果参数包含方法__dir__(),该方法将被调用。
如果参数不包含__dir__(),该方法将最大限度地收集参数信息。
获取属性后判断是否为大写,是则添加为配置参数
用类导入配置的作用
在开发和线上,往往采用的不是相同的配置文件。我们可以通过类封装几套配置文件以供使用。
可以编写一个基础类,在开发测试、线上运行都相同、都需要的配置参数。再通过继承,扩展不同环境下的不同配置参数。
则在不同的环境下,只需要改变from_object() 中的参数即可。
Flask如何处理请求
app程序对象
在一些Python web框架中,视图函数类似
@route('/')
def index():
return 'hello'
但在flask中
@app.route('/')
def index():
return 'hello'
flask 中存在一个显式的程序对象,我们需要在全局空间中创建它。设计原因主要包括:
- 相较于隐式程序对象,同一时间只能有一个实例存在,显式的程序对象允许多个程序实例存在。
- 允许通过子类化Flask类来改变程序行为。
- 允许通过工厂函数来创建程序实例,可以在不同的地方传入不同的配置来创建不同的程序实例。
- 允许通过蓝本来模块化程序。
启动app.run()
在Flask类中
当调用app.run(),程序启动。我们查看run()函数的源码
from werkzeug.serving import run_simple
try:
run_simple(host, port, self, **options)
finally:
# reset the first request information if the development server
# reset normally. This makes it possible to restart the server
# without reloader and that stuff from an interactive shell.
self._got_first_request = False
可见run_simple函数,而第三个参数是self,即flask对象。
当调用对象时,python会执行__call__
方法。
进入Flask() 类可以看到
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exce