starlette-flask


Namestarlette-flask JSON
Version 0.0.1 PyPI version JSON
download
home_page
SummarySession Middleware for Starlette/FastAPI Applications based on Flask Session Decoding and Encoding.
upload_time2023-12-30 08:34:57
maintainer
docs_urlNone
author
requires_python>=3.8
license
keywords asgi cookie fastapi flask session starlette
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # starlette-flask

[![PyPI - Version](https://img.shields.io/pypi/v/starlette-flask.svg)](https://pypi.org/project/starlette-flask)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/starlette-flask.svg)](https://pypi.org/project/starlette-flask)

-----

Session Middleware for Starlette/FastAPI Applications based on Flask Session Decoding and Encoding.

**Table of Contents**

- [starlette-flask](#starlette-flask)
  - [Installation](#installation)
  - [Story](#story)
  - [Real World Example](#real-world-example)
  - [License](#license)

## Installation

```console
pip install starlette-flask
```

## Story

I was migrating from [Flask] to [FastAPI] and I found out that I could use my existing [Flask] applications with [FastAPI] (thanks to [a2wsgi]) application.

> I must tell you this: Many of my [Flask] applications depend on third-party [Flask] extensions like [Flask Admin], [Flask Login], and [Flask-JWT-Extended]

So I searched how to couple [Flask] and [FastAPI] and [found a way](https://fastapi.tiangolo.com/advanced/wsgi/#using-wsgimiddleware). But there was a problem... I wasn't able to access the session data between [Flask] and [FastAPI] applications. I mean I was able to CRUD session data on the [Flask] side and [FastAPI] side but I couldn't CRUD the session data inside [FastAPI] that was CRUDed by [Flask], so I started this [discussion](https://github.com/tiangolo/fastapi/discussions/9318) in the [FastAPI] repository. Back then I wasn't able to solve this, so I decided not to use [Flask Login] and [Flask Admin] anymore...

But you can see that the discussion didn't get any answers from March to September. It was bothering me, so I decided to solve it myself. I took a look at the source code of [Flask] and [Starlette] (backend core of [FastAPI]). I found that they used different methods to sign the session data and [Starlette] kept re-signing the session data even if it wasn't created, updated, deleted, or even read, that was the problem... I needed a custom `SessionMiddleware` that uses the same method as [Flask] to sign the session data and I did implement it.

Here are some related discussions/issues/pull requests:

- [Sharing Session Data between Mounted Applications · tiangolo/fastapi · Discussion #9318](https://github.com/tiangolo/fastapi/discussions/9318)
- [Use Base64Url encoding and decoding in Session Cookie by allezxandre · Pull Request #1922 · encode/starlette](https://github.com/encode/starlette/pull/1922)
- [SessionMiddleware sends a new set-cookie for every request, with unintended results · Issue #2019 · encode/starlette](https://github.com/encode/starlette/issues/2019)
- [Create More Flexable SessionMiddleware · encode/starlette · Discussion #2256](https://github.com/encode/starlette/discussions/2256)
- [SessionMiddleware may create malformed cookie · Issue #1259 · encode/starlette](https://github.com/encode/starlette/issues/1259)
- [Added `allow_path_regex` to the `SessionMiddleware` by hasansezertasan · Pull Request #2316 · encode/starlette](https://github.com/encode/starlette/pull/2316)

Check out [Middleware - Starlette](https://www.starlette.io/middleware/) page to learn more about middlewares in [Starlette].

## Real World Example

So what's the problem? Let's say you have a [Flask] application and it was live for a long time. You want to migrate to [FastAPI] but you don't want your users to lose their session data. And to be honest, migrating is not an easy process. You might want to take it slow and get the benefit of FastAPI features like mounting an application to another application.

Let's try to mount a [Flask] application to a [FastAPI] application.

```python
from fastapi import FastAPI, Request, Response
from flask import Flask, jsonify, session, request
from starlette.middleware.sessions import SessionMiddleware
from a2wsgi import WSGIMiddleware

secret_key = "super-secret"


flask_app = Flask(__name__)
flask_app.config["SECRET_KEY"] = secret_key


@flask_app.get("/")
def flask_index():
    return jsonify({"message": "Hello World from Flask Application"})


@flask_app.get("/set-session")
def flask_set_session():
    session["application"] = "flask"
    session.modified = True
    return jsonify({"message": "Session set"})


@flask_app.get("/get-session")
def flask_get_session():
    return jsonify({"message": session.get("application", None)})


@flask_app.get("/delete-session")
def flask_delete_session():
    session.pop("application")
    session.modified = True
    return jsonify({"message": "Session deleted"})


@flask_app.before_request
def before_request():
    print(session.items())

@flask_app.after_request
def after_request(response):
    print(session.items())
    return response


fastapi_application = FastAPI()
fastapi_application.add_middleware(
    SessionMiddleware,
    secret_key="super-secret",
)


@fastapi_application.middleware("http")
async def starlette_add_process_time_header(request: Request, call_next):
    response = await call_next(request)
    response.headers["X-Process-Time"] = "100"
    print(response.headers)
    return response


@fastapi_application.get("/")
async def starlette_index(req: Request):
    return {"message": "Hello World from FastAPI Application"}


@fastapi_application.get("/set-session")
async def starlette_set_session(req: Request):
    req.session.update({"application": "fastapi"})
    return {"message": "Session set"}


@fastapi_application.get("/get-session")
async def starlette_get_session(req: Request):
    return {"message": req.session.get("application", None)}


@fastapi_application.get("/delete-session")
async def starlette_delete_session(req: Request):
    req.session.pop("application")
    return {"message": "Session deleted"}


app = FastAPI()
app.mount("/flask-application", WSGIMiddleware(flask_app))
app.mount("/fastapi-application", fastapi_application)

```

The problem here is this: If you set a session in [Flask] application, you can't get it from [FastAPI] application, and vice versa. At the same time, beyond accessing the session data, these two applications overwrite each other's session data. That's because they use different methods to sign the session data.

Since they use different methods to sign the session data, they can't decode each other's session data. What can we do? We can use `starlette-flask` to solve this problem.

All you need to do is this:

```diff
- from starlette.middleware.sessions import SessionMiddleware
+ from starlette_flask.middleware.sessions import SessionMiddleware
```

## License

`starlette-flask` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.

[FastAPI]: https://github.com/tiangolo/fastapi
[Starlette]: https://github.com/encode/starlette
[Flask]: https://github.com/pallets/flask
[Flask Admin]: https://github.com/flask-admin/flask-admin
[Flask Login]: https://github.com/maxcountryman/flask-login
[a2wsgi]: https://github.com/abersheeran/a2wsgi
[Flask-JWT-Extended]: https://github.com/vimalloc/flask-jwt-extended

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "starlette-flask",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "hasansezertasan <hasansezertasan@gmail.com>",
    "keywords": "asgi,cookie,fastapi,flask,session,starlette",
    "author": "",
    "author_email": "hasansezertasan <hasansezertasan@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/6c/a2/498f23652b710bab080f7ec1e1d911e4264f06f2dc108faac7a16d781ce5/starlette_flask-0.0.1.tar.gz",
    "platform": null,
    "description": "# starlette-flask\n\n[![PyPI - Version](https://img.shields.io/pypi/v/starlette-flask.svg)](https://pypi.org/project/starlette-flask)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/starlette-flask.svg)](https://pypi.org/project/starlette-flask)\n\n-----\n\nSession Middleware for Starlette/FastAPI Applications based on Flask Session Decoding and Encoding.\n\n**Table of Contents**\n\n- [starlette-flask](#starlette-flask)\n  - [Installation](#installation)\n  - [Story](#story)\n  - [Real World Example](#real-world-example)\n  - [License](#license)\n\n## Installation\n\n```console\npip install starlette-flask\n```\n\n## Story\n\nI was migrating from [Flask] to [FastAPI] and I found out that I could use my existing [Flask] applications with [FastAPI] (thanks to [a2wsgi]) application.\n\n> I must tell you this: Many of my [Flask] applications depend on third-party [Flask] extensions like [Flask Admin], [Flask Login], and [Flask-JWT-Extended]\n\nSo I searched how to couple [Flask] and [FastAPI] and [found a way](https://fastapi.tiangolo.com/advanced/wsgi/#using-wsgimiddleware). But there was a problem... I wasn't able to access the session data between [Flask] and [FastAPI] applications. I mean I was able to CRUD session data on the [Flask] side and [FastAPI] side but I couldn't CRUD the session data inside [FastAPI] that was CRUDed by [Flask], so I started this [discussion](https://github.com/tiangolo/fastapi/discussions/9318) in the [FastAPI] repository. Back then I wasn't able to solve this, so I decided not to use [Flask Login] and [Flask Admin] anymore...\n\nBut you can see that the discussion didn't get any answers from March to September. It was bothering me, so I decided to solve it myself. I took a look at the source code of [Flask] and [Starlette] (backend core of [FastAPI]). I found that they used different methods to sign the session data and [Starlette] kept re-signing the session data even if it wasn't created, updated, deleted, or even read, that was the problem... I needed a custom `SessionMiddleware` that uses the same method as [Flask] to sign the session data and I did implement it.\n\nHere are some related discussions/issues/pull requests:\n\n- [Sharing Session Data between Mounted Applications \u00b7 tiangolo/fastapi \u00b7 Discussion #9318](https://github.com/tiangolo/fastapi/discussions/9318)\n- [Use Base64Url encoding and decoding in Session Cookie by allezxandre \u00b7 Pull Request #1922 \u00b7 encode/starlette](https://github.com/encode/starlette/pull/1922)\n- [SessionMiddleware sends a new set-cookie for every request, with unintended results \u00b7 Issue #2019 \u00b7 encode/starlette](https://github.com/encode/starlette/issues/2019)\n- [Create More Flexable SessionMiddleware \u00b7 encode/starlette \u00b7 Discussion #2256](https://github.com/encode/starlette/discussions/2256)\n- [SessionMiddleware may create malformed cookie \u00b7 Issue #1259 \u00b7 encode/starlette](https://github.com/encode/starlette/issues/1259)\n- [Added `allow_path_regex` to the `SessionMiddleware` by hasansezertasan \u00b7 Pull Request #2316 \u00b7 encode/starlette](https://github.com/encode/starlette/pull/2316)\n\nCheck out [Middleware - Starlette](https://www.starlette.io/middleware/) page to learn more about middlewares in [Starlette].\n\n## Real World Example\n\nSo what's the problem? Let's say you have a [Flask] application and it was live for a long time. You want to migrate to [FastAPI] but you don't want your users to lose their session data. And to be honest, migrating is not an easy process. You might want to take it slow and get the benefit of FastAPI features like mounting an application to another application.\n\nLet's try to mount a [Flask] application to a [FastAPI] application.\n\n```python\nfrom fastapi import FastAPI, Request, Response\nfrom flask import Flask, jsonify, session, request\nfrom starlette.middleware.sessions import SessionMiddleware\nfrom a2wsgi import WSGIMiddleware\n\nsecret_key = \"super-secret\"\n\n\nflask_app = Flask(__name__)\nflask_app.config[\"SECRET_KEY\"] = secret_key\n\n\n@flask_app.get(\"/\")\ndef flask_index():\n    return jsonify({\"message\": \"Hello World from Flask Application\"})\n\n\n@flask_app.get(\"/set-session\")\ndef flask_set_session():\n    session[\"application\"] = \"flask\"\n    session.modified = True\n    return jsonify({\"message\": \"Session set\"})\n\n\n@flask_app.get(\"/get-session\")\ndef flask_get_session():\n    return jsonify({\"message\": session.get(\"application\", None)})\n\n\n@flask_app.get(\"/delete-session\")\ndef flask_delete_session():\n    session.pop(\"application\")\n    session.modified = True\n    return jsonify({\"message\": \"Session deleted\"})\n\n\n@flask_app.before_request\ndef before_request():\n    print(session.items())\n\n@flask_app.after_request\ndef after_request(response):\n    print(session.items())\n    return response\n\n\nfastapi_application = FastAPI()\nfastapi_application.add_middleware(\n    SessionMiddleware,\n    secret_key=\"super-secret\",\n)\n\n\n@fastapi_application.middleware(\"http\")\nasync def starlette_add_process_time_header(request: Request, call_next):\n    response = await call_next(request)\n    response.headers[\"X-Process-Time\"] = \"100\"\n    print(response.headers)\n    return response\n\n\n@fastapi_application.get(\"/\")\nasync def starlette_index(req: Request):\n    return {\"message\": \"Hello World from FastAPI Application\"}\n\n\n@fastapi_application.get(\"/set-session\")\nasync def starlette_set_session(req: Request):\n    req.session.update({\"application\": \"fastapi\"})\n    return {\"message\": \"Session set\"}\n\n\n@fastapi_application.get(\"/get-session\")\nasync def starlette_get_session(req: Request):\n    return {\"message\": req.session.get(\"application\", None)}\n\n\n@fastapi_application.get(\"/delete-session\")\nasync def starlette_delete_session(req: Request):\n    req.session.pop(\"application\")\n    return {\"message\": \"Session deleted\"}\n\n\napp = FastAPI()\napp.mount(\"/flask-application\", WSGIMiddleware(flask_app))\napp.mount(\"/fastapi-application\", fastapi_application)\n\n```\n\nThe problem here is this: If you set a session in [Flask] application, you can't get it from [FastAPI] application, and vice versa. At the same time, beyond accessing the session data, these two applications overwrite each other's session data. That's because they use different methods to sign the session data.\n\nSince they use different methods to sign the session data, they can't decode each other's session data. What can we do? We can use `starlette-flask` to solve this problem.\n\nAll you need to do is this:\n\n```diff\n- from starlette.middleware.sessions import SessionMiddleware\n+ from starlette_flask.middleware.sessions import SessionMiddleware\n```\n\n## License\n\n`starlette-flask` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.\n\n[FastAPI]: https://github.com/tiangolo/fastapi\n[Starlette]: https://github.com/encode/starlette\n[Flask]: https://github.com/pallets/flask\n[Flask Admin]: https://github.com/flask-admin/flask-admin\n[Flask Login]: https://github.com/maxcountryman/flask-login\n[a2wsgi]: https://github.com/abersheeran/a2wsgi\n[Flask-JWT-Extended]: https://github.com/vimalloc/flask-jwt-extended\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Session Middleware for Starlette/FastAPI Applications based on Flask Session Decoding and Encoding.",
    "version": "0.0.1",
    "project_urls": {
        "Changelog": "https://github.com/hasansezertasan/starlette-flask/CHANGELOG.md",
        "Documentation": "https://github.com/hasansezertasan/starlette-flask#readme",
        "Issues": "https://github.com/hasansezertasan/starlette-flask/issues",
        "Source": "https://github.com/hasansezertasan/starlette-flask"
    },
    "split_keywords": [
        "asgi",
        "cookie",
        "fastapi",
        "flask",
        "session",
        "starlette"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "842321d00bd58cbd2bc5a5b55e33b95b7fbc19bd21d51347504b85b5b0e64d83",
                "md5": "ce9224ef972ea58bbab5c3330e1a4c01",
                "sha256": "5d956a2d9be73c0e5f4167a82d0f43d0d0d2a2f691498c2286bc191978e627fd"
            },
            "downloads": -1,
            "filename": "starlette_flask-0.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ce9224ef972ea58bbab5c3330e1a4c01",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 6759,
            "upload_time": "2023-12-30T08:34:55",
            "upload_time_iso_8601": "2023-12-30T08:34:55.899608Z",
            "url": "https://files.pythonhosted.org/packages/84/23/21d00bd58cbd2bc5a5b55e33b95b7fbc19bd21d51347504b85b5b0e64d83/starlette_flask-0.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6ca2498f23652b710bab080f7ec1e1d911e4264f06f2dc108faac7a16d781ce5",
                "md5": "cd4bea6a1b4055bff5b1e4622b2ebdcf",
                "sha256": "c679892300c3ac21d9789cd0b1e50635a03271cd575846bb6670a6aa647c758b"
            },
            "downloads": -1,
            "filename": "starlette_flask-0.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "cd4bea6a1b4055bff5b1e4622b2ebdcf",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 9476,
            "upload_time": "2023-12-30T08:34:57",
            "upload_time_iso_8601": "2023-12-30T08:34:57.921319Z",
            "url": "https://files.pythonhosted.org/packages/6c/a2/498f23652b710bab080f7ec1e1d911e4264f06f2dc108faac7a16d781ce5/starlette_flask-0.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-30 08:34:57",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "hasansezertasan",
    "github_project": "starlette-flask",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "starlette-flask"
}
        
Elapsed time: 0.16895s