Интерпретация исходного кода Python Web Flask (2) — принцип маршрутизации

Python
Интерпретация исходного кода Python Web Flask (2) — принцип маршрутизации

обо мне
Небольшой программист в мире программирования, в настоящее время работает руководителем команды в предпринимательской команде.Стек технологий включает 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Найдите соответствующий метод и верните результат после выполнения.

0x03 Учебные материалы