CORS#
Cross origin resources usually handled by browsers in specific way.
Sources
CORS topic in fastapi documentation.
Cross-Origin Resource Sharing (CORS) page in MDN.
On this page we will look at an extremely simple example that shows where CORS errors occur and how to fix them using embedded fastapi tools. So we’ll look at a really popular case - there’s an API that expects to be accessed by a front-end running in a browser. So this is where CORS comes in - the browser won’t work with the results of the API response if it doesn’t have special headers.
import os
import docker
import requests
docker_client = docker.from_env()
Back-end#
The simplest possible backend application. It doesn’t use any middleware yet, to show that it won’t work if we just build it like this.
%%writefile cors_files/app.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def say_hello():
return "hello"
Overwriting cors_files/app.py
Front-end#
Consider the front-end application that we’ll use as an example. The main feature is that here we have Fetch Data from Backend
button that will cause fetching data from http://localhost:8000
where will be access to backend.
%%writefile cors_files/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Frontend</title>
</head>
<body>
<h1>Frontend</h1>
<button id="fetchData">Fetch Data from Backend</button>
<pre id="response"></pre>
<script>
document.getElementById('fetchData').addEventListener('click', () => {
fetch('http://localhost:8000')
.then(response => response.json())
.then(data => {
document.getElementById('response').textContent = JSON.stringify(data, null, 2);
})
.catch(error => console.error('Error:', error));
});
</script>
</body>
</html>
Overwriting cors_files/index.html
Containers#
Now let’s set up Docker containers: one for the front-end and one for the back-end.
frontend_container = docker_client.containers.run(
image = "httpd",
remove = True,
detach = True,
name = "test_front",
volumes = {
f"{os.getcwd()}/cors_files/index.html": {
"bind": "/usr/local/apache2/htdocs/index.html",
"mode": "rw"
}
},
ports={80: 8080}
)
backend_container = docker_client.containers.run(
image = "fastapi_experiment",
remove = True,
detach = True,
name = "test_back",
ports = {8000: 8000},
volumes = {
f"{os.getcwd()}/cors_files/app.py": {
"bind": "/app.py", "mode": "rw"
}
},
command = "uvicorn --host 0.0.0.0 --reload app:app"
)
Let’s check that the front-end is working by sending a request to it.
ans = requests.get("http://localhost:8080")
print(ans.content.decode("utf-8"))
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Frontend</title>
</head>
<body>
<h1>Frontend</h1>
<button id="fetchData">Fetch Data from Backend</button>
<pre id="response"></pre>
<script>
document.getElementById('fetchData').addEventListener('click', () => {
fetch('http://localhost:8000')
.then(response => response.json())
.then(data => {
document.getElementById('response').textContent = JSON.stringify(data, null, 2);
})
.catch(error => console.error('Error:', error));
});
</script>
</body>
</html>
And same with back-end.
ans = requests.get("http://localhost:8000")
print(ans.content.decode("utf-8"))
"hello"
Problem#
Now you can access the frontend at http://localhost:8080. When you press the “Fetch data from backend” button, you’ll see something like the following picture:
Your request failed because of CORS Missing Allow Origin
.
Modification of the API#
To avoid the problem we had in the previous examples, we need to use fastapi.middleware.cors.CORSMiddleware
. So here is a modification of the backend application.
%%writefile cors_files/app.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def say_hello():
return "hello"
Overwriting cors_files/app.py
If you now try to press the “Fetch data from backend” button - everything is fine.
Get cors headers#
What if we try to check if fastapi really adds cors? You need to add origin
- only then will fastapi return CORS headers.
In the following example, we’re printing headers for our API request.
requests.get("http://localhost:8000").headers
{'date': 'Mon, 29 Jul 2024 15:51:57 GMT', 'server': 'uvicorn', 'content-length': '7', 'content-type': 'application/json'}
The result doesn’t contain any headers related to the cors.
But after adding the origin
header to the request, we got headers with headers specific to CORS.
requests.get("http://localhost:8000", headers={'origin': ''}).headers
{'date': 'Mon, 29 Jul 2024 15:52:27 GMT', 'server': 'uvicorn', 'content-length': '7', 'content-type': 'application/json', 'access-control-allow-origin': '*', 'access-control-allow-credentials': 'true'}
So in the result we got headers access-control-allow-origin
and access-control-allow-credentials
.
Note don’t forget to stop containers after all.
frontend_container.stop()
backend_container.stop()