обо мне
Небольшой программист в мире программирования, в настоящее время работает руководителем команды в предпринимательской команде.Стек технологий включает Android, Python, Java и Go, который также является основным стеком технологий нашей команды.
Контакт: hylinux1024@gmail.com
Публичный аккаунт WeChat: гневный код
ловитьПредыдущийтема, продолжайте читатьFlaskИсходный код, взгляните на этот фреймворкПринцип маршрутизации.
0x00 Принцип маршрутизации
Первый взглядFlaskпростое использование
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return f'Hello, World!'
if __name__ == '__main__':
app.run()
существуетFlaskв использовании@app.routeЭтот декоратор реализуетurlи сопоставление между методами.
Flask.route
Открытьrouteметод
def route(self, rule, **options):
"""这个方法的注释非常详细,为了避免代码篇幅过长,这里省略注释"""
def decorator(f):
self.add_url_rule(rule, f.__name__, **options)
self.view_functions[f.__name__] = f
return f
return decorator
существуетrouteВ методе два параметраruleа такжеoptions.ruleдаurlправило,optionsПараметры в основномwerkzeug.routing.Ruleиспользование класса. Внутри также определен методdecoratorметод, будетurlПравила пути и соответствующие отношения между именами методов сохраняются, а затем имя метода функции и объект функции также сохраняются в словаре соответственно.
Flask.add_url_rule
def add_url_rule(self, rule, endpoint, **options):
options['endpoint'] = endpoint
options.setdefault('methods', ('GET',))
self.url_map.add(Rule(rule, **options))
Аннотация этого метода также очень подробная, что примерно означает, что метод определен
@app.route('/')
def index():
pass
Эквивалентно
def index():
pass
app.add_url_rule('index', '/')
app.view_functions['index'] = index
последний звонокurl_map.addметод будетruleа такжеoptionпостроен какRuleдобавить кMapв объекте.
Rule
Ruleвыражатьurlправила, этоwerkzeugКласс, определенный в библиотеке функций.
url_mapобычайMapобъект. его целью является достижениеurlОтображение отношений с методами.
Map.add
def add(self, rulefactory):
"""Add a new rule or factory to the map and bind it. Requires that the
rule is not bound to another map.
:param rulefactory: a :class:`Rule` or :class:`RuleFactory`
"""
for rule in rulefactory.get_rules(self):
rule.bind(self)
self._rules.append(rule)
self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
self._remap = True
существуетaddметод называетсяruleсерединаbindметод, вотОсознайте логику связывания.
Rule.bind
def bind(self, map, rebind=False):
"""Bind the url to a map and create a regular expression based on
the information from the rule itself and the defaults from the map.
:internal:
"""
if self.map is not None and not rebind:
raise RuntimeError('url rule %r already bound to map %r' %
(self, self.map))
# 将url与map对应起来,即将map保存在rule对象自身的map属性上
self.map = map
if self.strict_slashes is None:
self.strict_slashes = map.strict_slashes
if self.subdomain is None:
self.subdomain = map.default_subdomain
rule = self.subdomain + '|' + (self.is_leaf and self.rule or self.rule.rstrip('/'))
self._trace = []
self._converters = {}
self._weights = []
regex_parts = []
for converter, arguments, variable in parse_rule(rule):
if converter is None:
regex_parts.append(re.escape(variable))
self._trace.append((False, variable))
self._weights.append(len(variable))
else:
convobj = get_converter(map, converter, arguments)
regex_parts.append('(?P<%s>%s)' % (variable, convobj.regex))
self._converters[variable] = convobj
self._trace.append((True, variable))
self._weights.append(convobj.weight)
self.arguments.add(str(variable))
if convobj.is_greedy:
self.greediness += 1
if not self.is_leaf:
self._trace.append((False, '/'))
if not self.build_only:
regex = r'^%s%s$' % (
u''.join(regex_parts),
(not self.is_leaf or not self.strict_slashes) and \
'(?<!/)(?P<__suffix__>/?)' or ''
)
self._regex = re.compile(regex, re.UNICODE)
существуетbindв методеforвызывается в циклеparse_urlметод, который представляет собой функцию-генератор, использующую обычныйyieldвозвращает кортеж. Есть еще много деталей этого метода, но здесь мы сначала поймем основной контекст и проясним общий процесс.
существуетFlaskиз декоратора при запускеrouteначнетurlСоответствует методу функции ответа.
Логика вызова
Flask.route -> Flask.add_url_rule -> Map.add -> Rule.bind
0x01 Ответный запрос
После начала службыFlaskбудет включен по умолчаниюWebСервер удобен для разработки и отладки, может использоваться в реальной среде.nginx+gunicornи другие инструменты для развертывания. Поскольку развертывание не является темой этого раздела, давайте сосредоточимся на ответах на запросы клиентов.
существуетПредыдущиймы знаемFlaskпройти черезWerkzeugв библиотекеrun_simpleспособ запуска службы.
Этот метод будет выполнен, когда клиент отправит запрос
Flask.wsgi_app
def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied:
app.wsgi_app = MyMiddleware(app.wsgi_app)
: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
"""
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
environдаWebПараметры, переданные сервером,request_context(environ)Создаст экземпляр контекста запроса путем предварительной обработкиpreprocess_requestЗатем он войдет в запрос на распространениеdispatch_request, за которым следует ответ выполненияmake_responseа такжеprocess_response, и, наконец, вернутьсяresponse.
Здесь мы ориентируемся наdispatch_request.
Flask.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`.
"""
try:
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
except HTTPException as e:
handler = self.error_handlers.get(e.code)
if handler is None:
return e
return handler(e)
except Exception as e:
handler = self.error_handlers.get(500)
if self.debug or handler is None:
raise
return handler(e)
Ядром этого метода являетсяmatch_request, сопоставив запрос клиентаurlПравило находит соответствующий метод функции.
Flask.match_request
def match_request(self):
"""Matches the current request against the URL map and also
stores the endpoint and view arguments on the request object
is successful, otherwise the exception is stored.
"""
rv = _request_ctx_stack.top.url_adapter.match()
request.endpoint, request.view_args = rv
return rv
Он будет вызван после сопоставленияself.view_functions[endpoint](**values)чтобы выполнить соответствующий метод функции и вернуть возвращаемое значение функции.
Если вышеdispatch_requestне совпадаетurlправила будут реализованыerror_handlersНайдите соответствующий код ошибки в словаре и выполнитеhandlerметод.
слишком далекоurlПроцесс сопоставления правил маршрутизации завершен.
0x02 Суммировать
существуетFlaskПосле запуска будетrouteПосле разбора декоратора поместитеurlПравила и методы функций хранятся соответственно.
По запросу клиента,Flask.wsgi_appметод будет выполнен и начнет сопоставлениеurlНайдите соответствующий метод и верните результат после выполнения.