CORS Policy error on second render of React app from FastAPI backend
Asked Answered
B

3

3

I am working on a React frontend to chart some data from a fastapi backend. I am using a couple of dropdown components to change the month and year for the requested data. With the initial render the fetch request works fine and returns the data and the charts display. Once I change the dropdowns, I get the following CORS Policy Error in the browser console.

Access to fetch at 'https://fake-url.com/endpoint/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

React code snippet with the fetch call:

 const [month, setMonth] = useState(1);
 const [year, setYear] = useState('2023');
 const [revenueData, setRevenueData] = useState({});

 useEffect(() => {
     const inputs = {
     "month": month,
     "year": year,
     "duration": 1
     }

     const myHeaders = new Headers();
     myHeaders.append("X-API-KEY", "fake-api-key");
     myHeaders.append("Content-Type", "application/json");

     const requestOptions = {
         method: 'POST',
         headers: myHeaders,
         body: JSON.stringify(inputs),
         redirect: 'follow'
     };

     fetch("https://fake-url.com/endpoint/", requestOptions)
         .then(response => response.json())
         .then(data => {
             setRevenueData((data))
         }).catch(error => {
             console.log('error', error)
        });
 }, [month, year]);

I confirmed that I am using CORSMiddleware in fastapi with the following settings:

app.add_middleware(HTTPSRedirectMiddleware)

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_methods=['*'],
    allow_headers=['*']
)

I also confirmed that the backend is returning access-control headers for preflight with an options request in postman as shown: response in postman

UPDATE

The network panel shows that the second request preflight is successful but ultimately fails in an Internal Server Error. Which lead me to:

CORS and Internal Server Error responses

dev tools Network Panel

Bondmaid answered 1/11, 2023 at 18:12 Comment(3)
I'm going to assume the headers you are posting are from whatever is serving your web page rather than https://fake-url.com. You need to add the headers to the server https://fake-url.com. If you do not control or own https://fake-url.com you need to convince whoever does to add them. This is the whole purpose of CORS If this is not the case you need to explicitly add that information to your post to avoid confusion and more debugging details as to why your browser isn't respecting the headers it is given.Bestiary
@Bestiary Doing the best I can here. Your assumption is incorrect. Those are the headers returned from https://fake-url.com from the preflight request. I made a couple of words bold in the sentence prior to emphasize this. I get that CORS headers need to be coming from the backend server. Which is why I looked at my fastapi implementation first. Sorry it is a confusing situation for me. If it wasn't so, I probably would not be asking for help from the community here... can you point me in the direction of how I might troubleshoot further?Bondmaid
Use your browser's dev tools Network panel to inspect the requests. Compare the one that works with the one that doesn't. How do they differ besides just the request body? Do they have the same request headers? Perhaps the failing request has an error response without CORS headersSergent
M
4

CORS headers are not added when the request ends in an error, that is, when a response is returned with a status code such as 4xx or 5xx.

As shown in the screenshot you provided, when calling the /dashboard_data API endpoint for the third time, the server responds with 500 Internal Server Error response code, indicating that the server encountered an unexpected condition that prevented it from fulfilling the request. Hence, in that case, the server won't include the appropriate Access-Control-Allow-Headers / Access-Control-Allow-Origin headers, and the browser will report a CORS error in the devtools console. Thus, please check what causes the error on server side in the first place and try to fix it (the console running the server should give you insights on that).

Related answers to the concept of CORS issues that might prove helpful to you and future readers can be found here, as well as here and here.

Marbut answered 2/11, 2023 at 6:28 Comment(0)
I
0

You can resolve this question by the follow code, the solution is add cors manually in exception_handler.

app = FastAPI()

origins = [
    "http://localhost",
    "http://localhost:8080",
    # 其他你希望允许的源
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def read_root():
    return {"message": "Hello World"}

@app.get("/error")
async def create_error():
    raise ValueError("This is a test error")

@app.exception_handler(Exception)
async def exception_handler(request: Request, exception: Union[Exception, RuntimeError]):
    headers = {
        'Access-Control-Allow-Origin': ', '.join(origins),
        'Access-Control-Allow-Credentials': 'true',
        'Access-Control-Allow-Methods': '*',
        'Access-Control-Allow-Headers': '*',
    }
    if isinstance(exception, EntityException):
        response = JSONResponse(
            jsonable_encoder(
                {
                    "code": exception.code,
                    "message": exception.message,
                    "exception": exception.exception
                }
            ),
            headers=headers
        )
    else:
        response = JSONResponse(
            jsonable_encoder(
                {
                    "exception": str(exception),
                    "code": 500,
                }
            ),
            headers=headers
        )
    return response

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
Idelson answered 1/6, 2024 at 10:36 Comment(2)
From where did you import EntityException?Dhiman
It's my customized Exception which you can find easily on FastAPI official doc.Idelson
D
0

If you want to manage errors more precisely and customize the headers to prevent this issue, the following approach proved effective for me. I faced this problem because of an incorrect data type in the payload.

from fastapi import FastAPI, Request, status
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):

    headers = {
        'Access-Control-Allow-Origin'      : ', '.join(origins),
        'Access-Control-Allow-Credentials' : 'true',
        'Access-Control-Allow-Methods'     : '*',
        'Access-Control-Allow-Headers'     : '*',
    }

    exc_str = ''

    for err in exc._errors:
        exc_str += f"{err['msg']} for input `{err['input']}` in the field {'->'.join(err['loc'])}. "

    return JSONResponse(
        jsonable_encoder(
            {
                "status_code" : 422,
                "message"     : exc_str,
                "exception"   : "HTTP_422_UNPROCESSABLE_ENTITY"
            }
        ),
        status_code = status.HTTP_422_UNPROCESSABLE_ENTITY,
        headers = headers
    )

Make sure it is placed before any of your routes.

Dhiman answered 11/6, 2024 at 12:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.