Incoming query params without value are modified
Describe the bug
I am working with a remote service that expects (among other things) a query parameter without value, eg "&bla".
However when going through the reverse proxy, the call made adds an = sign, which renders the call invalid for that remote service (over which I have no control)
To Reproduce
- create a file main.py
from fastapi_proxy_lib.core.http import ReverseHttpProxy
from httpx import AsyncClient
from starlette.requests import Request
import logging
logging.basicConfig(level=logging.DEBUG)
proxy = ReverseHttpProxy(AsyncClient(), base_url="https://www.httpbin.org/")
app = FastAPI()
@app.get("/get")
async def get(request: Request):
return await proxy.proxy(request=request, path="get")
- Run with
uvicorn main:app --host 127.0.0.1 --port 8000 - Call http://127.0.01:8000/get?bla
- Notice in the logs that the call is made to https://www.httpbin.org/get?bla=, instead of https://www.httpbin.org/get?bla
Expected behavior
Query parameters should remain unchanged, even if they have no value
Configuration
- Python version: 13
- OS version: MacOS 15.5
- deps version: annotated-types==0.7.0 anyio==4.6.2.post1 Brotli==1.1.0 certifi==2024.8.30 charset-normalizer==3.4.0 click==8.1.7 dnspython==2.7.0 email_validator==2.2.0 fastapi==0.111.1 fastapi-cli==0.0.5 fastapi-proxy-lib==0.3.0 h11==0.14.0 httpcore==1.0.6 httptools==0.6.4 httpx==0.27.2 httpx-ws==0.7.2 idna==3.10 Jinja2==3.1.4 loguru==0.7.2 markdown-it-py==3.0.0 MarkupSafe==3.0.2 mdurl==0.1.2 pydantic==2.9.2 pydantic_core==2.23.4 Pygments==2.18.0 python-dotenv==1.0.1 python-multipart==0.0.17 PyYAML==6.0.2 requests==2.32.3 rich==13.9.4 shellingham==1.5.4 sniffio==1.3.1 starlette==0.37.2 typer==0.13.0 typing_extensions==4.12.2 urllib3==2.2.3 uvicorn==0.29.0 uvloop==0.21.0 watchfiles==0.24.0 websockets==13.1 wsproto==1.2.0
I believe the issue comes from the way the request is built, through the use of query_params
proxy_request = client.build_request(
method=request.method,
url=target_url,
params=tuple(request.query_params.multi_items()),
headers=proxy_header,
content=request_content, # FIXME: 一个已知问题是,流式响应头包含'transfer-encoding': 'chunked',但有些服务器会400拒绝这个头
# cookies=request.cookies, # NOTE: headers中已有的cookie优先级高,所以这里不需要
)
Hi @wabiloo, thank you for reporting this. I would say the current behavior is expected.
The use of &bla as a query parameter is ambiguous. Different frameworks may interpret it differently: as {"bla": ""}, {"bla": None}, or {"bla": True}.
fastapi interprets it as {"bla": ""}. I'm not sure how your "remote service" interprets it.
We can't improve this situation because fastapi cannot distinguish between &bla and &bla=. For your specific case, you may need to use:
- https://wsh032.github.io/fastapi-proxy-lib/Usage/Advanced/#modify-request
- https://www.python-httpx.org/advanced/authentication/#custom-authentication-schemes
You might need to manually construct the URL, since as far as I know, using httpx.Request.params will not generate a URL like https://www.httpbin.org/get?bla (without the =).
Alternatively, you could contact the remote service and ask them to handle this case more flexibly.