[Python] Deploying my FastAPI app to Azure, but routes don't work anymore

Discussão em 'Python' iniciado por Stack, Dezembro 16, 2024.

  1. Stack

    Stack Membro Participativo

    I succesfully deployed my FastAPI app to azure, but when I try to access the routes, it says 404 not found. However, when I test the same route locally it works. My db is hosted on azure. I tried adding a startup.sh file with this command: gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app, without any luck.

    Thhis is my database.py:

    import pyodbc
    import logging
    import sys

    # Set up logging to output to stdout (which Azure captures)
    logging.basicConfig(level=logging.INFO, stream=sys.stdout, format='%(asctime)s - %(levelname)s - %(message)s')
    logger = logging.getLogger(__name__)

    def get_db_connection():
    server = '//' # Azure SQL server name
    database = '//' # Database name
    username = '//' # Username
    password = '//' # Password
    driver = '{ODBC Driver 18 for SQL Server}' # Driver for SQL Server

    # Create a connection string
    connection_string = f'DRIVER={driver};SERVER={server};PORT=1433;DATABASE={database};UID={username};PWD={password}'

    try:
    # Log the attempt to connect
    logger.info("Attempting to connect to the database...")

    # Establish the connection
    conn = pyodbc.connect(connection_string)

    # Log success
    logger.info("Successfully connected to the database.")

    return conn
    except Exception as e:
    # Log any connection error
    logger.error(f"Failed to connect to the database: {e}")
    raise


    My main.py:

    from fastapi import FastAPI, Query, HTTPException
    import pyodbc
    from database import get_db_connection
    import traceback
    import logging

    # Set up logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)

    app = FastAPI()

    def get_user_organizational_unit_code() -> str:
    # This is the hardcoded default organizational unit code
    return "0000-0010"

    @app.get("/chargers")
    async def get_chargers(organizationalUnitCode: str = Query(..., description="The organizational unit code to filter chargers")):
    """
    Fetch chargers based on the provided organizational unit code.
    """
    try:
    if not organizationalUnitCode:
    raise HTTPException(status_code=400, detail="No organizationalUnitCode provided.")

    # Log the received organizational unit code
    logger.info(f"Received organizationalUnitCode: {organizationalUnitCode}")

    # Establish the connection to the database
    conn = get_db_connection()
    cursor = conn.cursor()

    # Log the query being executed
    logger.info(f"Executing query for organizationalUnitCode: {organizationalUnitCode}")

    # Query chargers based on the organizational unit code
    cursor.execute("""
    SELECT *
    FROM [dbo].[chargepointStatus]
    WHERE organizationalUnitCode = ?
    """, organizationalUnitCode)

    # Fetch all rows from the query result
    rows = cursor.fetchall()

    # Log the number of results found
    logger.info(f"Query returned {len(rows)} results for organizationalUnitCode: {organizationalUnitCode}")

    # If no rows are returned, raise a 404 exception
    if not rows:
    logger.warning(f"No chargers found for the provided organizational unit code: {organizationalUnitCode}")
    return {"detail": "No chargers found for the provided organizational unit code."}

    # Format the result
    chargers = [{
    "ID": row[0],
    "chargepointID": row[1],
    "name": row[2],
    "connector": row[3],
    "location": row[4],
    "status": row[5],
    "statusError": row[6],
    "statusTime": row[7],
    "networkStatus": row[8],
    "networkStatusTime": row[9],
    "mailContactOffline": row[10],
    "mailContactStatus": row[11],
    "mailContactOfflineLate": row[12],
    "organizationalUnitCode": row[13],
    "organizationalUnitName": row[14],
    } for row in rows]

    # Log the result
    logger.info(f"Returning {len(chargers)} chargers for organizationalUnitCode: {organizationalUnitCode}")

    # Close the cursor and the connection
    cursor.close()
    conn.close()

    return {"chargers": chargers}

    except HTTPException as http_err:
    # Log HTTP-specific errors
    logger.error(f"HTTP error occurred: {http_err.detail}")
    return {"detail": http_err.detail}

    except Exception as e:
    # Log the exception for debugging
    logger.error(f"Error: {e}")
    logger.error(traceback.format_exc())
    return {"detail": "Internal Server Error", "error": str(e)}


    My yaml file for deployment:

    name: Build and deploy Python app to Azure Web App - watkanikladenapi

    on:
    push:
    branches:
    - main
    workflow_dispatch:

    jobs:
    build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python version
    uses: actions/setup-python@v5
    with:
    python-version: '3.10'
    - name: Clear pip cache
    run: pip cache purge

    - name: Install system dependencies for pyodbc
    run: sudo apt-get install -y unixodbc-dev g++ python3-dev

    - name: Create and start virtual environment
    run: |
    python -m venv venv
    source venv/bin/activate

    - name: Install dependencies
    run: pip install -r requirements.txt

    # Optional: Add step to run tests here (PyTest, Django test suites, etc.)

    - name: Zip artifact for deployment
    run: zip release.zip ./* -r

    - name: Upload artifact for deployment jobs
    uses: actions/upload-artifact@v4
    with:
    name: python-app
    path: |
    release.zip
    !venv/

    deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
    name: 'Production'
    url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
    permissions:
    id-token: write # This is required for requesting the JWT

    steps:
    - name: Download artifact from build job
    uses: actions/download-artifact@v4
    with:
    name: python-app

    - name: Unzip artifact for deployment
    run: unzip release.zip

    - name: Login to Azure
    uses: azure/login@v2
    with:
    client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID= }}
    tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID }}
    subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID}}

    - name: 'Deploy to Azure Web App'
    uses: azure/webapps-deploy@v3
    id: deploy-to-webapp
    with:
    app-name: 'watkanikladenapi'
    slot-name: 'Productio


    '

    and my requirements.txt:

    fastapi==0.95.0
    uvicorn==0.20.0
    pyodbc==4.0.34 --find-links https://github.com/mkleehammer/pyodbc/releases
    gunicorn==20.0.4

    Continue reading...

Compartilhe esta Página