5

In the frontend, I have the following JS function:

export const uploadFormData = async (
    token: string,
    email: string,
    formInfo: Array<Object>,
): Promise<any> => {
    const formData = new FormData();
    formData.append('email', email);
    formData.append('form_info', JSON.stringify({ formInfo }));
    return fetch(
        `${process.env.ENDPOINT}/upload_form_data/`,
        {
            method: 'POST',
            headers: {
                Authorization: `Token ${token}`,
            },
            body: formData,
        },
    ).then((response) => {
        console.log(response.body?.getReader());
        if (response.status === 404) {
            throw Error('Url not found');
        }
        if (response.status === 422) {
            throw Error('Wrong request format');
        }
        if (response.status !== 200) {
            throw Error('Something went wrong with uploading the form data.');
        }
        const data = response.json();
        return {
            succes: true,
            data,
        };
    }).catch((error) => Promise.reject(error));
};

which sends a POST request to the following endpoint in the FastAPI backend:

@app.post("/api/queue/upload_form_data/")
async def upload_form_data(
    email: str = Body(...),  
    form_info: str = Body(...), 
    authorization: str = Header(...),
    
):
    return 'form data processing'

However, it keeps throwing the following errors:

  1. In the frontend:

    POST http://localhost:8000/api/queue/upload_form_data/ 422 (Unprocessable Entity)
    Uncaught (in promise) Error: Wrong request format
    
  2. In the backend:

    POST /api/queue/upload_form_data/ HTTP/1.1" 400 Bad Request
    
  3. In Swagger UI (response body):

    {
      "detail": [
        {
          "loc": [
            "header",
            "authorization"
          ],
          "msg": "field required",
          "type": "value_error.missing"
        }
      ]
    }
    

What is wrong with the request that is causing these errors?

Chris
  • 18,724
  • 6
  • 46
  • 80
benjammin
  • 83
  • 1
  • 5
  • The body of the 422 error will tell you exactly what value is missing and why the request is failing; look in your browser's development tools under Network to see the actual request being sent to the server (and the response with the body). – MatsLindh Oct 16 '22 at 15:35

1 Answers1

6

In Swagger/OpenAPI specification, Authorization is a reserved header—along with Accept and Content-Type headers as well—for Swagger's built-in authentication/authorization functionality (see Swagger documentation). Hence, they are not allowed to be defined.

If you are using Swagger, you can't have an Authorization header parameter defined in your endpoint and expect it to work, as it would be ignored when submitting the request through Swagger UI, and you would receive a 422 Unprocessable Entity error with a body message saying that the authorization header is miising (just like the error posted in your question).

Solutions

Solution 1

If you don't need Swagger UI for testing your application, you can leave it as is and keep using JavaScript Fetch API, passing the Authorization in the headers. For example:

from fastapi import Header

@app.post('/')
def main(authorization: str = Header(...)):
    return authorization

Also, note that you don't really have to define any Header parameters in your endpoint, as you can always access them directly through the Request object, for instance:

from fastapi import Request

@app.post('/')
def main(request: Request):
    authorization = request.headers.get('Authorzation')
    return authorization

Solution 2

If you do need this to work with Swagger UI as well, one solution would be to use FastAPI's HTTPBearer, which would allow you to click on the Authorize button at the top right hand corner of your screen in Swagger UI autodocs (at /docs), where you can type your API key in the Value field. This will set the Authorization header in the request headers. Example:

from fastapi import FastAPI, Depends
from fastapi.security import HTTPBearer

app = FastAPI()
security = HTTPBearer()

@app.get('/')
def main(authorization: str = Depends(security)):
    return authorization.credentials

Alternatively, you could use APIKeyHeader with the desired name for the header (in your case, that is, Authorization), which would also allow you to click on the Authorize button in Swagger UI, in order to set the header value.

from fastapi import FastAPI, Security
from fastapi.security.api_key import APIKeyHeader

app = FastAPI()
api_key_header = APIKeyHeader(name='Authorization')

@app.get('/')
def main(api_key: str = Security(api_key_header)):
    return api_key 

Solution 3

Another solution would be to rename the authorization Header parameter to something else, e.g., token, and define this parameter as Optional in your API endpoint. Then, inside your endpoint, check if the API key is in either the token or request.headers.get('Authorization') parameter—if both return None, then this means that no token was provided. Example:

from fastapi import FastAPI, Request, Header
from typing import Optional

app = FastAPI()

@app.get('/')
def main(request: Request, token: Optional[str] = Header(None)):
    authorization = request.headers.get('Authorization')
    return token if token else authorization  
Chris
  • 18,724
  • 6
  • 46
  • 80