You can't declare an endpoint that expects both JSON
and File
/Form
data together, as this is not supported by the HTTP
protocol, as explained in FastAPI's documentation. When a request includes Form
data, it will have the body
encoded using application/x-www-form-urlencoded
instead of application/json
; if File
data are included as well, it will have the body
encoded using multipart/form-data
.
As explained in this answer, there are various methods to send additional data together with uploading Files
. The below demonstrates an approach based on Method 4 of the above answer.
Note: You shouldn't name your python script file fastapi.py (as shown in your question), as this would interfere with the library (when using, for example, from fastapi import FastAPI
), but rather use some neutral name, such as app.py.
app.py
from fastapi import FastAPI, File, UploadFile, Body
from pydantic import BaseModel, conint
from typing import Optional
import json
app = FastAPI()
class Rate(BaseModel):
id1: int
id2: int
message: Optional[str] = None
rate: conint(ge=1, le=5)
@classmethod
def __get_validators__(cls):
yield cls.validate_to_json
@classmethod
def validate_to_json(cls, value):
if isinstance(value, str):
return cls(**json.loads(value))
return value
@app.post("/rate")
def submit(user_review: Rate = Body(...), image: UploadFile = File(...)):
return {"JSON Payload ": user_review, "Image": image.filename}
test.py
import requests
url = 'http://127.0.0.1:8000/rate'
file = {'image': open('image.png','rb')}
data = {'user_review': '{"id1": 1, "id2": 2, "message": "foo", "rate": 5}'}
resp = requests.post(url=url, data=data, files=file)
print(resp.json())
Test with Fetch API (using Jinja2Templates
or HTMLResponse
)
<html>
<head>
<script type="text/javascript">
function submitData() {
var fileInput = document.getElementById('imageFile');
if (fileInput.files[0]) {
var data = new FormData();
data.append("user_review", JSON.stringify({id1: 1, id2: 2, message: "foo", rate: 5}));
data.append("image", fileInput.files[0]);
fetch('/rate', {
method: 'POST',
headers: {
'Accept': 'application/json'
},
body: data
})
.then(resp => resp.text()) // or, resp.json(), etc.
.then(data => {
document.getElementById("responseArea").innerHTML = data;
})
.catch(error => {
console.error(error);
});
}
}
</script>
</head>
<body>
<input type="file" id="imageFile" name="file"></br></br>
<input type="button" value="Submit" onclick="submitData()">
<div id="responseArea"></div>
</body>
</html>