Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WsgiFunctionApp with DispatcherMiddleware and registering new HTTP triggers blueprints #213

Closed
mycaule opened this issue Apr 23, 2024 · 1 comment

Comments

@mycaule
Copy link

mycaule commented Apr 23, 2024

Using a technique called DispatcherMiddleware it is possible to mount a Flask app only at an arbitrary prefix path.

Would it be possible for the http_app_func to manage only this mount path so that the app can register other HTTP endpoints (using durable functions and orchestrators for example)

import azure.functions as func
from werkzeug.serving import run_simple
from werkzeug.middleware.dispatcher import DispatcherMiddleware

flask_app = Flask(__name__)
@flask_app.get("/")
def hello():
    return Response(f"<h1>Hello World</h1>", mimetype="text/html")

flask_app.wsgi_app = DispatcherMiddleware(run_simple, {'/abc/123': flask_app.wsgi_app})
# see https://stackoverflow.com/a/18967744/1360476

app = func.WsgiFunctionApp(app=flask_app.wsgi_app, http_auth_level=func.AuthLevel.ANONYMOUS)
# app.register_blueprint(other_http_bp_here)

It appears every routes become managed by http_app_func because of the constant route template below.

@self.http_type(http_type='wsgi')
@self.route(methods=(method for method in HttpMethod),
auth_level=self.auth_level,
route="/{*route}")
def http_app_func(req: HttpRequest, context: Context):
return wsgi_middleware.handle(req, context)

Moreover using the host.json file it looks like it is only possible to set the $.extensions.http.routePrefix globally

https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook?tabs=isolated-process%2Cfunctionsv2&pivots=programming-language-python#hostjson-settings

@mycaule mycaule changed the title WsgiFunctionApp with DispatcherMiddleware and WsgiFunctionApp with DispatcherMiddleware and registering new HTTP triggers blueprints Apr 23, 2024
@mycaule
Copy link
Author

mycaule commented Apr 26, 2024

Adding this class in my project solved the problem.

prefix = "v1"

class FlaskFunctionApp(ExternalHttpFunctionApp):
    """A class to create a WsgiFunctionApp object."""

    def __init__(self, app, http_auth_level: AuthLevel | str = AuthLevel.FUNCTION):
        """Constructor of WsgiFunctionApp object."""

        super().__init__(auth_level=http_auth_level)
        self._add_http_app(WsgiMiddleware(app))

    def _add_http_app(self, http_middleware: AsgiMiddleware | WsgiMiddleware) -> None:
        """Add a Wsgi app integrated http function."""

        if not isinstance(http_middleware, WsgiMiddleware):
            raise TypeError("Please pass WsgiMiddleware instance" " as parameter.")

        wsgi_middleware: WsgiMiddleware = http_middleware

        @self.http_type(http_type="wsgi")
        @self.route(methods=(method for method in HttpMethod), auth_level=self.auth_level, route=prefix + "/{*route}")
        def http_app_func(req: HttpRequest, context: Context):
            return wsgi_middleware.handle(req, context)

I then just needed to mount using the same prefix.

from werkzeug.exceptions import NotFound

middleware = DispatcherMiddleware(NotFound(), {f"/{prefix}": flask_app.wsgi_app})
app = FlaskFunctionApp(app=middleware, http_auth_level=func.AuthLevel.ANONYMOUS)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant