В нашей команде мы использовали 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
?