Description
There is encoding issues when using AsgiMiddleware toghether with FastApi. Swedish characters like åäö
are not handeled correctly.
The issue might come from this line: https://github.com/Azure/azure-functions-python-library/blob/e1d3ebbad3eb3a17a41839ba7b866e56096febf6/azure/functions/_http_wsgi.py#L33
See mwe at Source section.
Investigative information
Please provide the following:
- Timestamp:
2024-02-28T11:27:27.886Z
- Function App name: N/A
- Function name(s) (as appropriate):
backend-api
- Core Tools version:
4.0.5455
Repro steps
Provide the steps required to reproduce the problem:
- Run the function app with code from source #section
func start -p 8000
- Post a
GET
request tohttp://localhost:8000/hello/Pippi%20L%C3%A5ngstrump
(Pippi Långstrump is Pippi Longstocking in swedish)
Expected behavior
Provide a description of the expected behavior.
The response should be:
{
"name": "Pippi Långstrump",
"encoded name": "Pippi Långstrump"
}
Actual behavior
Provide a description of the actual behavior observed.
{
"name": "Pippi LÃ¥ngstrump",
"encoded name": "Pippi Långstrump"
}
Do note that the following is seen in the func log:
$ func start -p 8000
Found Python version 3.10.12 (python3).
Azure Functions Core Tools
Core Tools Version: 4.0.5455 Commit hash: N/A (64-bit)
Function Runtime Version: 4.27.5.21554
[2024-02-28T11:27:27.886Z] Customer packages not in sys path. This should never happen!
[2024-02-28T11:27:28.430Z] Worker process started and initialized.
Functions:
backend-api: http://localhost:8000/{*route}
For detailed output, run func with --verbose flag.
[2024-02-28T11:27:30.308Z] Executing 'Functions.backend-api' (Reason='This function was programmatically called via the host APIs.', Id=4151d47b-9d32-4d64-afd3-0deabf68f9ed)
[2024-02-28T11:27:30.345Z] http://localhost:8000/hello/Pippi%20L%C3%A5ngstrump {'route': 'hello/Pippi Långstrump'}
[2024-02-28T11:27:30.345Z] Pippi LÃ¥ngstrump Pippi Långstrump
[2024-02-28T11:27:30.388Z] Executed 'Functions.backend-api' (Succeeded, Id=4151d47b-9d32-4d64-afd3-0deabf68f9ed, Duration=97ms)
[2024-02-28T11:27:32.598Z] Host lock lease acquired by instance ID '000000000000000000000000B7E20425'.
So the encoding is right in line 26 (print(req.url, req.route_params)
-> [2024-02-28T11:27:30.345Z] http://localhost:8000/hello/Pippi%20L%C3%A5ngstrump {'route': 'hello/Pippi Långstrump'}
) but something happens when the parameter is sent to FastAPI (line 11 print(name, name.encode("latin-1").decode("utf-8"))
-> [2024-02-28T11:27:30.345Z] Pippi LÃ¥ngstrump Pippi Långstrump
). The expected behavior is seen when FastAPI is invoked with uvicorn.
Known workarounds
Provide a description of any known workarounds.
See line 14 name.encode("latin-1").decode("utf-8")
so there is an encoding from bytes to latin-1
somewhere in your code, Python3 is UTF-8.
Contents of the requirements.txt file:
Provide the requirements.txt file to help us find out module related issues.
annotated-types==0.6.0
anyio==4.3.0
azure-functions==1.18.0
exceptiongroup==1.2.0
fastapi==0.109.0
idna==3.6
pydantic==2.6.3
pydantic_core==2.16.3
sniffio==1.3.1
starlette==0.35.1
typing_extensions==4.10.0
Related information
Provide any related information
Source
# function_app.py
"""Main app"""
import azure.functions as func
from fastapi import FastAPI
fast_app = FastAPI()
@fast_app.get("/hello/{name}")
async def get_name(name: str):
print(name, name.encode("latin-1").decode("utf-8"))
return {
"name": name,
"encoded name": name.encode("latin-1").decode("utf-8"),
}
app = func.FunctionApp()
# Azure Functions v2 HTTP trigger decorator
@app.function_name("backend-api")
@app.route(route="{*route}", auth_level="anonymous")
async def backend_api(req: func.HttpRequest) -> func.HttpResponse:
"""Initiate backend_api as AsgiMiddleware"""
print(req.url, req.route_params)
return await func.AsgiMiddleware(fast_app).handle_async(req) # pragma: no cover
requirements.txt
annotated-types==0.6.0
anyio==4.3.0
azure-functions==1.18.0
exceptiongroup==1.2.0
fastapi==0.109.0
idna==3.6
pydantic==2.6.3
pydantic_core==2.16.3
sniffio==1.3.1
starlette==0.35.1
typing_extensions==4.10.0
host.json
{
"version": "2.0",
"extensions": {
"http": {
"routePrefix": ""
}
}
}