Как я могу заменить объект запроса Flask чем-то другим, прежде чем передать его в обработчик маршрута? - PullRequest
0 голосов
/ 21 ноября 2019

В нашей команде мы использовали Flask для всех наших HTTP-приложений и создали несколько вспомогательных библиотек для всего этого. Теперь у нас есть приложение, использующее aiohttp, которое было выбрано для упрощения разработки с помощью веб-сокетов (это первое приложение, использующее веб-сокеты).

Мы адаптировали нашу самую важную библиотеку для прозрачной поддержки flask и * 1005. * что позволило нам избежать большого количества дублирования кода (авторизация, централизованное ведение журналов и мониторинг, общие настройки и т. д.)

Одним из этих шагов было создание обертки вокруг flask.wrappers.Request и aiohttp.web_request.Request, чтобы иметьобщий API.

Все это работает в коде aiohttp, и, удаляя все ненужные ошибки, сводится к следующему:

"""
Request wrapper for both Flask and aiohttp request, exposing a comming API between both classes
"""

from typing import Any, Set

from floss.auth.roles import Role
from floss.auth.token import JWTOptions

try:
    import flask
except ImportError:
    flask = None  # type: ignore

try:
    import aiohttp  # type: ignore
except ImportError:
    aiohttp = None


class RequestWrapper:
    """
    A class which wraps either an aiohttp or flask Request object and acts like
    a Facade
    """

    @staticmethod
    def construct(request: Any) -> 'RequestWrapper':
        if aiohttp and isinstance(request, aiohttp.web_request.Request):
            return WrappedAiohttpRequest(request)
        if flask and isinstance(request, flask.wrappers.Request):
            return WrappedFlaskRequest(request)
        raise TypeError('%r is an unsupported request type!' % request)

    def __init__(self, wrapped_request: Any) -> None:
        self.wrapped_request = wrapped_request
        ... (setting the common attributes)


class WrappedAiohttpRequest(RequestWrapper):

    def __init__(self, wrapped_request: Any) -> None:
        super().__init__(wrapped_request)
        ... (mapping aiohttp specifics to needed attributes)


class WrappedFlaskRequest(RequestWrapper):

    def __init__(self, wrapped_request: Any) -> None:
        super().__init__(wrapped_request)
        ... (mapping flask specifics to needed attributes)

А затем для aiohttp

from aiohttp import web
from aiohttp.web import (
    Application,
    HTTPException,
    Response,
    WebSocketResponse,
    middleware
)

from floss.compat import RequestWrapper

@middleware
async def wrap_request(request, handler):
    wrapped_request = RequestWrapper.construct(request)
    response = await handler(wrapped_request)
    return response


# Example application
app = Application(middlewares=[wrap_request])

Приведенный выше код гарантирует, что каждый маршрут будет вызываться с экземпляром RequestWrapper вместо обычного aiohttp.web_request.Request экземпляра. Мы знаем, что это скрывает некоторые атрибуты, если они не отображаются в классе RequestWrapper. После взвешивания взлетов и падений в нашей команде было принято коллективное решение о его реализации таким образом.

Но в настоящее время я не вижу способа вызывать упаковщик подобным образом во Flask. .

Общая библиотека в настоящее время предоставляет подкласс Flask с общим поведением. Я знаю, что вы можете установить Flask.request_class , но я бы хотел этого избежать, потому что это означает, что любой, кто имеет подкласс подкласса нашего собственного подкласса Flask, не может установить это значение сам, не нарушая ничего. Так что это кажется немного хрупким. Это должно работать , хотя, так что это вариант.

Существует также full_dispatch_request , который кажется хорошим кандидатом, пока вы не поймете, что это невозможно, потому что это невозможно. Flask сохраняет запрос в глобальной переменной. И я не думаю, что вы можете заменить это так легко.

before_request имеет ту же проблему глобальной переменной.

Использование werkzeug middlewares - этотакже (насколько я могу судить) не вариант, потому что они не знают о специфике Flask и делают свои манипуляции на уровне HTTP (поправьте меня, если я ошибаюсь здесь).

Итак, этоесть ли способ сделать это так же чисто, как в aiohttp?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...