alex-oleshkevich / starsessions

Advanced sessions for Starlette and FastAPI frameworks
MIT License
98 stars 11 forks source link

Issue with SessionAutoloadMiddleware #57

Closed sasha-id closed 2 years ago

sasha-id commented 2 years ago

Hi Alex,

Trying to use starsessions with SessionAutoloadMiddleware and getting this erorr:

INFO:     127.0.0.1:51099 - "GET / HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/uvicorn/protocols/http/h11_impl.py", line 404, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/uvicorn/middleware/debug.py", line 106, in __call__
    raise exc from None
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/uvicorn/middleware/debug.py", line 103, in __call__
    await self.app(scope, receive, inner_send)
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starlette/middleware/cors.py", line 84, in __call__
    await self.app(scope, receive, send)
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starception/middleware.py", line 54, in __call__
    raise exc
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starception/middleware.py", line 38, in __call__
    await self.app(scope, receive, _send)
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starsessions/middleware.py", line 159, in __call__
    await load_session(connection)
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starsessions/session.py", line 49, in load_session
    await get_session_handler(connection).load()
  File "/Users/sash/Library/Caches/pypoetry/virtualenvs/core-W7t3oa5Y-py3.10/lib/python3.10/site-packages/starsessions/session.py", line 39, in get_session_handler
    return typing.cast(SessionHandler, connection.scope["session_handler"])
KeyError: 'session_handler'

Here's my starlette setup:

def main():
    return Server(
        db_url=Config.POSTGRES_URL,
        debug=DEBUG,
        controllers=[PagesController],
    )

if __name__ == "__main__":
    uvicorn.run(
        f"{__name__}:main",
        debug=DEBUG,
        factory=True,
        reload=DEBUG,
        # log_config=log_config,
        host='0.0.0.0',
        port=int(listen_port),
        log_level="debug" if DEBUG else "info",
    )
from sqlalchemy.exc import DatabaseError
from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
from starlette.staticfiles import StaticFiles
from starlette.routing import Mount
from starception import StarceptionMiddleware
from starsessions import CookieStore, SessionAutoloadMiddleware, SessionMiddleware
from imia import AuthenticationMiddleware, SessionAuthenticator

from core_utils.config import Config
from app.auth.user_provider import AuthUserProvider
from app.database import DatabaseManager
from app.exceptions import RequestError
from app.errors import on_error
from app.protocol import HttpMethod
from app.config.settings import STATIC_DIR

class Server(Starlette):
    def __init__(self, db_url, controllers, *args, debug=False, **kwargs):
        # TODO: Implement proper logging
        # logging.basicConfig(level=logging.DEBUG if debug else logging.INFO, stream=sys.stderr)
        # logging.config.dictConfig(yaml.load(open("app/config/logging.yml").read(), Loader=yaml.FullLoader))
        # self.log = logging.getLogger(__name__)
        super(Server, self).__init__(*args, **kwargs)
        self.debug = debug

        # Initialize database
        self.db = DatabaseManager(db_url, debug)

        # Session store
        session_store = CookieStore(secret_key=Config.SECRET)

        # Middlewares
        self.add_middleware(SessionMiddleware, store=session_store, cookie_name="s", lifetime=300, rolling=True)
        self.add_middleware(SessionAutoloadMiddleware)
        # ^^^^^^^^ Problem somewhere here ^^^^^^^^
        # self.add_middleware(AuthenticationMiddleware, authenticators=[SessionAuthenticator(user_provider=AuthUserProvider)])
        self.add_middleware(StarceptionMiddleware)
        self.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])  # CORS

        # Static route
        self.routes.append(Mount("/static", StaticFiles(directory=STATIC_DIR), name="static"))

        # Register controllers with app
        for ctrl_cls in controllers:
            self._controller_register(ctrl_cls)

        # Event handlers
        self.add_event_handler("startup", self.on_app_start)
        self.add_event_handler("shutdown", self.on_app_stop)

        # Error handlers
        self.add_exception_handler(RequestError, on_error)
        self.add_exception_handler(DatabaseError, on_error)
        self.add_exception_handler(Exception, on_error)

       # Etc.

Looks like the problem is here: https://github.com/alex-oleshkevich/starsessions/blob/4b86cad2cb02d21981e79399f74067e45fc9ba9f/starsessions/middleware.py#L61 Because of scope["type"] == "lifespan" the rest of the code is ignored. Am I missing something? Can you point me in the right direction?

alex-oleshkevich commented 2 years ago

add_middleware adds middleware in reverse order. So in your case you have to use this order:

  1. SessionAutoloadMiddleware
  2. SessionMiddleware
alex-oleshkevich commented 2 years ago

Also, add_middleware is deprecated and it's no longer a recommended way to attach a middleware.

sasha-id commented 2 years ago

Thank you! it's working in reverse order. What's the recommended way to add middleware from the inside of inherited(Starlette) class?

alex-oleshkevich commented 2 years ago

call super.__init__(middleware=[...])

sasha-id commented 2 years ago

Perfect, thank you!