最基本的flask项目代码如下
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
简单来说,声明Flask对象,声明view function,运行flask对象。
看一下后台做了什么。
看一下Flask对象的__init__方法:
#: Holds the path to the instance folder.
#: .. versionadded:: 0.8
self.instance_path =
instance_path
#: The configuration
dictionary as :class:`Config`. This
behaves
#: exactly like a regular
dictionary but supports additional methods
#: to load a config from files.
self.config =
self.make_config(instance_relative_config)
#: A dictionary of
all view functions registered.
self.view_functions
= {}
#: A dictionary of
all registered error handlers.
self.error_handler_spec
= {}
#: A list of
functions that are called when:meth:`url_for`raises a
#: :exc:`~werkzeug.routing.BuildError
self.url_build_error_handlers
= []
#: A dictionary with
lists of functions that will be called at the
#: beginning of each request.
#use the :meth:`before_request`:
decorator.
self.before_request_funcs
= {}
#: A list of
functions that will be called at the beginning of the
#: first request to this
instance.
#: :meth:`before_first_request`
decorator.
self.before_first_request_funcs
= []
#: A dictionary with
lists of functions that should be called after
#: each request.
#:
:meth:`after_request` decorator.
self.after_request_funcs
= {}
#: A dictionary with
lists of functions that are called after
#: each request, even if an
exception has occurred.
#: :meth:`teardown_request`
decorator.
self.teardown_request_funcs
= {}
#: A list of
functions that are called when the application context
#: is destroyed.
self.teardown_appcontext_funcs
= []
#: A dictionary with
lists of functions that are called before the
#: :attr:`before_request_funcs`
functions.
#: :meth:`url_value_preprocessor`.
self.url_value_preprocessors
= {}
#: A dictionary with
lists of functions that can be used as URL value
#: preprocessors.
#:meth:`url_defaults`
self.url_default_functions
= {}
#: A list of shell
context processor functions that should be run
#: when a shell context is
created.
self.shell_context_processors
= []
#: all the attached
blueprints in a dictionary by name.
Blueprints
#: can be attached multiple times
so this dictionary does not tell
#: you how often they got
attached.
#:
#: .. versionadded:: 0.7
self.blueprints = {}
self._blueprint_order = []
#: The :class:`~werkzeug.routing.Map` for this instance. You can use
#: this to change the routing converters after the class was created
#: but before any routes are connected.
self.url_map = Map()
self.url_map.host_matching = host_matching
self.subdomain_matching = subdomain_matching
把路径及其它环境变量设置去掉以后,剩下的基本就这些了。
都是一些函数列表,用于在不同时机处理请求。
view_functions保存视图函数,error_handlers保存的错误处理函数
url_map保存uri到视图函数的映射。
顺带讲一下route装饰器:
def route(self, rule, **options):
"""A decorator that is used to register a view function for a
given URL rule. This does the same thing as :meth:`add_url_rule`
but is intended for decorator usage::
@app.route(‘/‘)
def index():
return ‘Hello World‘
"""
def decorator(f):
endpoint = options.pop(‘endpoint‘, None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
route装饰器的作用是将视图函数与url的对应联系装入self.url_map。
上面说到的是初始化部分,下面看服务器运行部分,当执行app.run()时:
找到run()方法,它做的事情很少,只是设定的一些参数,然后调用了run_simple方法:
def run(self, host=None, port=None, debug=None,
load_dotenv=True, **options):
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
def inner():
try:
fd = int(os.environ[‘WERKZEUG_SERVER_FD‘])
except (LookupError, ValueError):
fd = None
srv = make_server(hostname, port, application, threaded,
processes, request_handler,
passthrough_errors, ssl_context,
fd=fd)
if fd is None:
log_startup(srv.socket)
srv.serve_forever()
这里只列出了核心部分,前面有一些判断处理语句,按序做了以下工作:
def make_server(host=None, port=None, app=None, threaded=False, processes=1,
request_handler=None, passthrough_errors=False,
ssl_context=None, fd=None):
"""Create a new server instance that is either threaded, or forks
or just processes one request after another.
"""
if threaded and processes > 1:
raise ValueError("cannot have a multithreaded and "
"multi process server.")
elif threaded:
return ThreadedWSGIServer(host, port, app, request_handler,
passthrough_errors, ssl_context, fd=fd)
elif processes > 1:
return ForkingWSGIServer(host, port, app, processes, request_handler,
passthrough_errors, ssl_context, fd=fd)
else:
return BaseWSGIServer(host, port, app, request_handler,
passthrough_errors, ssl_context, fd=fd)
基本上就是根据服务器情况创建一个server instance。
继续,选择BaseWSGIServer()去看一下
class BaseWSGIServer(HTTPServer, object):
"""Simple single-threaded, single-process WSGI server."""
multithread = False
multiprocess = False
request_queue_size = LISTEN_QUEUE
def __init__(self, host, port, app, handler=None,
passthrough_errors=False, ssl_context=None, fd=None):
if handler is None:
handler = WSGIRequestHandler
self.address_family = select_ip_version(host, port)
if fd is not None:
real_sock = socket.fromfd(fd, self.address_family,
socket.SOCK_STREAM)
port = 0
HTTPServer.__init__(self, get_sockaddr(host, int(port),
self.address_family), handler)
self.app = app
self.passthrough_errors = passthrough_errors
self.shutdown_signal = False
self.host = host
self.port = self.socket.getsockname()[1]
# Patch in the original socket.
if fd is not None:
self.socket.close()
self.socket = real_sock
self.server_address = self.socket.getsockname()
if ssl_context is not None:
if isinstance(ssl_context, tuple):
ssl_context = load_ssl_context(*ssl_context)
if ssl_context == ‘adhoc‘:
ssl_context = generate_adhoc_ssl_context()
# If we are on Python 2 the return value from socket.fromfd
# is an internal socket object but what we need for ssl wrap
# is the wrapper around it :(
sock = self.socket
if PY2 and not isinstance(sock, socket.socket):
sock = socket.socket(sock.family, sock.type, sock.proto, sock)
self.socket = ssl_context.wrap_socket(sock, server_side=True)
self.ssl_context = ssl_context
else:
self.ssl_context = None
def log(self, type, message, *args):
_log(type, message, *args)
def serve_forever(self):
self.shutdown_signal = False
try:
HTTPServer.serve_forever(self)
except KeyboardInterrupt:
pass
finally:
self.server_close()
def handle_error(self, request, client_address):
if self.passthrough_errors:
raise
return HTTPServer.handle_error(self, request, client_address)
def get_request(self):
con, info = self.socket.accept()
return con, info
上面的部分是服务器的声明及运行,下面写一下flask服务器是如何处理请求的。
WSGI部分暂且略过,具体可看:https://www.cnblogs.com/steinliber/p/5133386.html
总而言之,它通过application_iter = app(environ, start_response)将请求体传给了flask的app。
接下来,当http请求从server发送过来的时候,会调用__call__()方法,最后实际是调用了wsgi_app功能并传入environ和start_response。
代码如下:
def wsgi_app(self, environ, start_response):
"""The actual WSGI application.
:param environ: A WSGI environment.
:param start_response: A callable accepting a status code,
a list of headers, and an optional exception context to
start the response.
"""
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
response是在这里生成的。
上下文处理语句:
ctx = self.request_context(environ)
核心语句:
response = self.full_dispatch_request()
找到full_dispatch_request:
def full_dispatch_request(self):
"""Dispatches
the request and on top of that performs request
pre and postprocessing as well as
HTTP exception catching and
error handling.
"""
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception as
e:
rv =
self.handle_user_exception(e)
return self.finalize_request(rv)
try_trigger_before_first_request_function()用于判断是否是第一个请求,然后是否执行
for func in self.before_first_request_funcs:
func()
request_started是信号机制通知请求开始处理,preprocess_request会调用app中注册的请求前函数,若函数的返回值不是None,response的内容就设为该返回值。否则就调用dispatch_request来找到对应的视图函数得到返回值
preprocess_request()方法的话,主要是进行flask的hook钩子, before_request功能的实现;
下一句:
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
一个http请求到了这里,实际上已经完成了从wsgi部分的过渡,进入到了寻找响应的阶段了,一个请求通过url进来以后,app怎么知道要如何响应呢?
就是通过dispatch_request方法来进行请求判定和分发。
def dispatch_request(self):
"""Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
be a response object. In order to convert the return value to a
proper response object, call :func:`make_response`.
"""
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
# if we provide automatic options for this URL and the
# request came with the OPTIONS method, reply automatically
if getattr(rule, ‘provide_automatic_options‘, False) \
and req.method == ‘OPTIONS‘:
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args)
self.view_functions是通过路由模块产生的endpoint与视图函数相对应的字典。这个就能返回视图函数要返回的值。
接下来返回full_dispatch_request
return self.finalize_request(rv)
对响应进行处理,主要是标准化,通过make_response来将其转化为response的对象
def finalize_request(self, rv, from_error_handler=False):
"""Given the return value from a view function this finalizes
the request by converting it into a response and invoking the
postprocessing functions. This is invoked for both normal
request dispatching as well as error handlers.
Because this means that it might be called as a result of a
failure a special safe mode is available which can be enabled
with the `from_error_handler` flag. If enabled, failures in
response processing will be logged and otherwise ignored.
:internal:
"""
response = self.make_response(rv)
try:
response = self.process_response(response)
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception(‘Request finalizing failed with an ‘
‘error while handling an error‘)
return response
对flask程序结构有了初步了解,理解了从请求到WSGI到flask的处理流程。
从请求到响应的流程图:
https://www.jianshu.com/p/2a2407f66438
https://blog.csdn.net/bestallen/article/details/54342120
原文:https://www.cnblogs.com/wodeboke-y/p/10587995.html