最基本的flask項目代碼以下html
from flask import Flaskshell
app = Flask(__name__)flask
@app.route("/")服務器
def hello():app
return "Hello World!"dom
if __name__ == "__main__":socket
app.run()ide
簡單來講,聲明Flask對象,聲明view function,運行flask對象。函數
看一下後臺作了什麼。post
看一下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