2

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 '[email protected];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 '[email protected];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 '[email protected];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 '[email protected];type=image/jpeg' \
  -F 'model={
  "model_name": "detr",
  "tags": [
    "balloon", "dog"
  ]
}' 

which fails with a "POST /detr2 HTTP/1.1" 422 Unprocessable Entity error

2
  • Should your payload not also contain model_name? Commented Mar 8, 2022 at 17:14
  • I am assuming the default model_name: str = "detr" would be pick if not set - but I might be wrong ! Commented Mar 8, 2022 at 18:10

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.