I am using FastAPI to serve some ML models and I have a Streamlit
basic UI using Python Requests
module.
One of my service is getting an image through a POST request and it is working like a charm.
Server side
@app.post("/segformer") async def segformer(file: Optional[UploadFile] = None):
curl given by {BASE_URI}/docs
curl -X 'POST' 'http://localhost:8080/segformer' -H 'accept: application/json' -H 'Content-Type: multipart/form-data' -F 'file=@image1.jpg;type=image/jpeg'
Client side using Python Requests
response = requests.post(f"{BASE_URI}/segformer", files=files)
As soon as I want to add additional parameters, it gets creepy. For example:
Server side
@dataclass class ModelInterface: model_name: str = "detr" tags: List[str] = Query(...) @app.post("/detr") async def detr(file: UploadFile = File(...), model: ModelInterface = Depends()):
curl — given by {BASE_URI}/docs
curl -X 'POST' 'http://localhost:8080/detr?model_name=detr&tags=balloon&tags=dog' -H 'accept: application/json' -H 'Content-Type: multipart/form-data' -F 'file=@image1.jpg;type=image/jpeg'
Until that point everything works fine. Once I want to convert that call using Python Requests
, I have some issues, which always end up in /detr HTTP/1.1" 400 Bad Request
error.
Here is what I’ve tried:
Client side using Python Requests
headers = { "Content-type": "multipart/form-data", "accept": "application/json" } payload = { "tags": ["balloon", "dog"] } response = requests.post( f"{BASE_URI}/detr", headers=headers, json=payload, <- also *data* files=files, )
At the end it seems that the problem narrows on how to convert this:
curl -X 'POST' 'http://localhost:8080/detr?tags=balloon&tags=dog' -H 'accept: application/json' -H 'Content-Type: multipart/form-data' -F 'file=@image1.jpg;type=image/jpeg'
to a valid Python Requests
call!
I also faced the issue that the following FastAPI code:
@dataclass class ModelInterface: model_name: str = "detr" tags: List[str] = None @app.post("/detr2") async def detr2(file: UploadFile = File(...), model: ModelInterface = Form(...)): ...
translates to this curl
command:
curl -X 'POST' 'http://localhost:8080/detr2' -H 'accept: application/json' -H 'Content-Type: multipart/form-data' -F 'file=@image1.jpg;type=image/jpeg' -F 'model={ "model_name": "detr", "tags": [ "balloon", "dog" ] }'
which fails with a "POST /detr2 HTTP/1.1" 422 Unprocessable Entity
error
Advertisement
Answer
To pass query parameters in Python requests, you should use params
key instead. Hence:
response = requests.post(url='<your_url_here>', params=payload)
Additionally, there is no need to set the Content-type
in the headers, as it will automatically be added, based on the parameters you pass to requests.post()
. Doing so, the request will fail, as, in addition to multipart/form-data
,”Content-type
must include the boundary
value used to delineate the parts in the post body. Not setting the Content-Type
header ensures that requests sets it to the correct value” (ref). Have a look at this and this. Also, make sure that in files
you use the same key
name you gave in your endpoint, i.e., file
. Thus, in Python requests it should look something like the below:
files = {('file', open('my_file.txt', 'rb'))} response = requests.post(url='<your_url_here>', files=files, params=payload)
You could also find more options as to how to send additional data along with files at this answer.