Skip to content

400 Bad Request when uploading multiple files+ Form data #103

@RahulDas-dev

Description

@RahulDas-dev

Description
When uploading multiple PDF files to the /api/v1/uploads endpoint, the server returns a 400 Bad Request error with the message "The browser (or proxy) sent a request that this server could not understand."

Expected Behavior

The server should accept multiple file uploads with the passwords parameter and return a 201 status code with information about the saved files.

Actual Behavior

The server receives the multipart form data correctly (as seen in the logs) but returns a 400 Bad Request error.

Code

import asyncio
import logging
from typing import List, Self

from pydantic import BaseModel, model_validator
from pydantic.dataclasses import dataclass
from quart import Blueprint, current_app, request
from quart_schema import DataSource, validate_request, validate_response
from quart_schema.pydantic import File

from library.extensions import pdf_loader

bp = Blueprint("uploads", __name__, url_prefix="/uploads")

logger = logging.getLogger(__name__)


class UploadReqst(BaseModel):
    passwords: list[str]   # Tried with List from typings also
    documents: list[File] # Tried with List from typings also


class UploadResponse(BaseModel):
    message: str


class ErrorResponse(BaseModel):
    error: str


# Kept for Deubing purpose only
@bp.before_request
async def log_request():
    if request.method == "POST":
        form_data = await request.form
        logger.debug(f"Received form data: {form_data}")
        # Don't use this in production, just for debugging
        files = await request.files
        logger.debug(f"Received files: {files}")


@bp.route("/", methods=["POST"])
@validate_request(UploadReqst, source=DataSource.FORM_MULTIPART) # Tried with  source=DataSource.MULTIPART 
@validate_response(UploadResponse, 201)
async def post(data: UploadReqst) -> tuple:
    logger.info(f"Total No of documents uploaded {len(data.documents)} ")
    uploads_list = []
    for data_item in data.documents:
        logger.info(f"File name: {data_item.filename}")
        uploads_list.append(pdf_loader.save(data_item, name=data_item.filename))
    saved_list = await asyncio.gather(*uploads_list)
    logger.info(f"Saved list: {len(saved_list)}")
    return UploadResponse(message=f"Saved list: {len(saved_list)}"), 201

Response Body From Swager

<!doctype html>
<html lang=en>
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>

Response Header From Swager

content-length: 167 
 content-type: text/html; charset=utf-8 
 date: Mon,28 Apr 2025 16:24:11 GMT 
 server: hypercorn-h11 
 x-env: DEVELOPMENT 
 x-language: en-US 
 x-timezn: UTC 
 x-version: 1.0.0 

Server Logs

2025-04-28 16:24:11,360.360 DEBUG [MainThread] [uploads.py:36] - Received form data: ImmutableMultiDict([('passwords', 'stringzafaf')])
2025-04-28 16:24:11,360.360 DEBUG [MainThread] [uploads.py:39] - Received files: ImmutableMultiDict([('documents', <FileStorage: 'Invoice-Copy-16.pdf' ('application/pdf')>), ('documents', <FileStorage: 'Invoice_TAPL_ER_23_300.pdf' ('application/pdf')>)])
[2025-04-28 16:24:11 +0000] [5552] [INFO] 127.0.0.1:62563 POST /api/v1/uploads/ 1.1 400 167 22594

dependency

>>> import quart
>>> import quart_schema
>>> import pydantic
>>> from importlib.metadata import version
>>>
>>> print(f'quart vesrion {version("quart")}')
quart vesrion 0.20.0
>>> print(f'quart_schema vesrion {version("quart_schema")}')
quart_schema vesrion 0.22.0
>>> print(f'pydantic vesrion {version("pydantic")}')
pydantic vesrion 2.11.1

github link - https://github.com/RahulDas-dev/workflow

How to make this Work ??

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions