422 Unprocessable Entity error in FastAPI when I try to upload an image using React frontend

642 views Asked by At

this code works perfectly when i hit this api using swagger ui. but fails to perform when its being hit from a react frontend showing status code 422 unprocessable entity. below is my fastapi endpoint-


@post.post('/post')
async def create_post(post_text: str, image: bytes = File(None), token: str = Depends(oauth2_scheme), db: Session = Depends(database.get_db)):
    user_info = await services.get_user_info(token)
    username = user_info["username"]
    print(username)
    image_url = None
    if image:
        # Generate a unique filename for the image
        image_filename = f"{username}_{uuid.uuid4().hex}.jpg"
        # print(image_filename)

        # Save the image to bytes and send to MinIO bucket
        # image_bytes = await image.read()
        image_bytes = base64.b64decode(image.split(",")[1])

        minio_client.put_object(
            "minilinkedindistributed",
            image_filename,
            io.BytesIO(image_bytes),  
            length=len(image_bytes),
            content_type="image/jpeg"
        )

        # Construct the image URL based on MinIO server URL and bucket name
        image_url = f"http://127.0.0.1:9000/minilinkedindistributed/{image_filename}"
        print(image_url)
    elif image is None or image.filename == '':
        raise HTTPException(status_code=400, detail='Invalid or empty image file')
    # Create the post
    new_post = services.make_post(db, username, post_text, image_url)

    headers = {"Authorization": f"Bearer {token}"}

    # Get all users (except the one who posted)
    async with httpx.AsyncClient() as client:
        response = await client.get("http://127.0.0.1:8000/api/v1/all_users_except_poster", headers=headers)

    if response.status_code == 200:
        all_users_except_poster = response.json()
    else:
        raise HTTPException(status_code=response.status_code, detail="Failed to fetch users from user service")

    # Create a notification for each user
    for user_to_notify in all_users_except_poster:
        notification_data = {
            'notification_text': f"{username} made a new post...",
            'pid' : new_post.id,
            'username' : user_to_notify["username"],
            'notification_datetime' : datetime.utcnow().isoformat(),
            'is_read' : False
        }

        
        async with httpx.AsyncClient() as client:
            response = await client.post("http://127.0.0.1:8002/api/v1/notification", json=notification_data, headers=headers)

        if response.status_code != 200:
            raise HTTPException(status_code=response.status_code, detail="Failed to send notification")
    

    return{"message" : new_post}

and below is my react component-



const HomePage = () => {
  const [posts, setPosts] = useState([]);
  const [loggedIn, setLoggedIn] = useState(false);
  const [postText, setPostText] = useState('');
  const [selectedImage, setSelectedImage] = useState(null);
  const [notifications, setNotifications] = useState([]);
  const navigate = useNavigate();

  useEffect(() => {
    const access_token = localStorage.getItem('access_token');
    if (access_token) {
      setLoggedIn(true);
      fetchPosts(access_token);
    }
  }, []);


  const handlePostTextChange = (event) => {
    setPostText(event.target.value);
  };

  const handleImageChange = (event) => {
    setSelectedImage(event.target.files[0]);
  };

  const handleSubmitPost = async () => {
    const formData = new FormData();
    formData.append('post_text', postText); // Make sure 'postText' holds the user's post text
    if (selectedImage) {
      formData.append('image', selectedImage, selectedImage.name); // Make sure 'selectedImage' holds the user's selected image
    }else console.log('Invalid or empty image file');
    console.log("Post Text:", postText);
    console.log("Selected Image:", selectedImage);
    console.log('FormData:', formData);

    const access_token = localStorage.getItem('access_token');
  
    // Make a POST request to create a new post
    try {
      const response = await axios.post(`${API_BASE_URL}/post`, formData, {
        headers: {
          Authorization: `Bearer ${access_token}`,
          'Content-Type': 'multipart/form-data', // Set the correct content type for the form data
        },
      });

      // Refresh the posts after successful submission
      fetchPosts(access_token);
      
    } catch (error) {
      console.error('Error submitting post:', error.response);
    }

  };

stuck here since ages, any solution will help. thank you

i tried googling and also saw some stackoverflow solutions but i tried all of them still it didnt work. even chatgpt failed to solve it. Need the solution as fast as possible

1

There are 1 answers

1
Chris On BEST ANSWER

In general, the 422 (unprocessable entity) response includes an error message about exactly which part/value in your request is missing or doesn't match the expected format. Hence, please make sure to have a look at that and include it in your question.

In your case, you seem to be using axios in the frontend to post Form data to the FastAPI backend. However, the way the post_text argument is defined in your endpoint is expected as query, not Form, parameter.

post_text expected as query parameter

@app.post('/create')
async def create_post(post_text: str):
    pass

As described in this answer, as well as here and here (please refer to those posts for more details and examples):

When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.

Hence, if you would like post_text to be a Form parameter, it should instead be declared as follows:

post_text expected as Form parameter

from fastapi import Form

@app.post('/create')
async def create_post(post_text: str = Form(...)):
    pass

Related answers demonstrating how to post File/Form data to FastAPI backend through axios or fetch request in the frontend can be found here, here, as well as here, here and here.