Azure / azure-functions-python-worker

Python worker for Azure Functions.
http://aka.ms/azurefunctions
MIT License
335 stars 103 forks source link

FastAPI Middlware Not routing on Azure Function [Premium] #1152

Closed BejanSadeghian closed 1 year ago

BejanSadeghian commented 1 year ago

Describe the bug Trying to get custom middleware working to log a request as it's coming in. In my code the request seems to run in the order expected but for whatever reason including custom middleware has the function always return a 500 (unless I hard code a func.HTTPResponse in the main function). Logging points are all reached (within the middleware and in the endpoint itself).

When I remove the middleware the app works fine and the app works fine with and without middleware locally but when deployed only the non-middleware version works.

To Reproduce Steps to reproduce the behavior: Using Python 3.9.x, locally using a MacBook pro (tested on M1 and i9), Azure Function is Consumption type with the 3.9 runtime. key packages: Starlette==0.20.4, fastapi==0.86.0, azure-functions==1.12.0

Sample code is below. Ive copied this code nearly verbatim from Microsoft's docs. Only change Ive introduced besides the middleware is I updated the function file to point to main.py instead of __init__.py.

Ive included both working and non-working code for comparison.

# Working Version of main.py (no middleware)
import azure.functions as func
import fastapi

app = fastapi.FastAPI()

@app.get("/sample")
async def index():
    logging.info("Got to sample!")
    return {
        "info": "Try /hello/Shivani for parameterized route.",
    }

@app.get("/hello/{name}")
async def get_name(name: str):
    return {
        "name": name,
    }

async def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    """Each request is redirected to the ASGI handler."""
    return await func.AsgiMiddleware(app).handle_async(req, context)
# Not working middleware version of main.py
import azure.functions as func
import fastapi
from fastapi import Request
import logging

app = fastapi.FastAPI()

@app.middleware("http")
async def simple_middleware(request: Request, call_next):
    logging.info(f"Request {request.method} {request.url}")
    response = await call_next(request)
    return response

@app.get("/sample")
async def index():
    logging.info("Got to sample!")
    return {
        "info": "Try /hello/Shivani for parameterized route.",
    }

@app.get("/hello/{name}")
async def get_name(name: str):
    return {
        "name": name,
    }

async def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    """Each request is redirected to the ASGI handler."""
    return await func.AsgiMiddleware(app).handle_async(req, context)
// these files are the same for both versions of the main.py file
// function.json
{
  "scriptFile": "main.py",
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ],
      "route": "{*route}"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

// host.json
{
  "version": "2.0",
  "extensions": {
    "http": {
        "routePrefix": ""
    }
  }
}

Expected behavior Id expect a 200 response to come back when sending a GET to https://{appname}}.azurewebsites.net/sample (browser or postman). This is the case without middleware. But with middleware I receive a 500 Server error. Locally both return the 200.

Screenshots Screen capture of the browser when the middleware version fails. image

Here is the logs from the not working middleware version. You can see its logging as expected so I believe the issue between whats returned by the call_next and how the function is responding.

image

Additional context Ive dropped logging in all places in my actual code. Before and after the call_next function everything seems to behave correctly. And the call_next is returning a real value. I submitted this request here because I believe Im doing something wrong with the function config or theres some sort of incompatibility with the function service and the starlette / fastAPI package. Im not entirely sure. In my actual code Im reaching to a Database (SQLAlchemy ORM) and I thought it was related to that not working well with async. But as you can see with this sample code the issue is present without anything advanced.

I have also tested removing all async and await in my app and changed the ASGIMiddleware method from handle_async to handle without any luck. I think FastAPI still tries to handle things Async but thought Id share this detail anyway.

Finally, for what its worth a non-custom middleware like the fastapi/starlette CORS doesnt cause issues but it also doesnt actually apply. I had to set CORS in azure portal instead.

YunchuWang commented 1 year ago

@BejanSadeghian Hi, thanks for reporting this issue, i believe this issue is same as https://github.com/Azure/azure-functions-python-worker/issues/1104 and we just had a fix merged for it. Unfortunately the fix will be released in next January.

ramya894 commented 1 year ago

Closing it as answered.

BejanSadeghian commented 1 year ago

Thank you @YunchuWang i appreciate the quick response and feedback. Do you have an idea on when the release will be coming? Is it 1.13?

bhagyshricompany commented 1 year ago

@BejanSadeghian will update you soon on this update