fastapi
fastapi copied to clipboard
CORSMiddleware not work
Hi, I searched the FastAPI documentation( https://fastapi.tiangolo.com/tutorial/cors/). But get errors: Access to XMLHttpRequest at "http://127.0.0.1:8086/api" from origin "http://www.example.com" 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 requeste resource.
"http://127.0.0.1:8086/api" and "http://www.example.com" are in same pc.
from fastapi import FastAPI from starlette.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=[""],
allow_credentials=True,
allow_methods=[""],
allow_headers=["*"],
)
How do you make the request? Could you use the web devtools to see how the request look like (with HTTP headers)?
Also you may want to follow the issue template.
How do you make the request? Could you use the web devtools to see how the request look like (with HTTP headers)?
Also you may want to follow the issue template.
var apiUrl = "http://127.0.0.1:8086/api";
var data-from = { };
$.ajax({
url: apiUrl,
type: 'POST',
data: JSON.stringify(data-from),
contentType: 'application/json;charset=utf-8',
dataType: 'json',
cache: false,
crossDomain: true,
async: true,
success: function (data) {
alert(JSON.stringify(data));
},
error: function (data, err) {
alert("fail " + JSON.stringify(data) + " " + JSON.stringify(err));
}
});
Client: Accept: / Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36 Access-Control-Request-Headers: content-type Access-Control-Request-Method: POST
Response: HTTP/1.1 502 Fiddler
You should add CORSMiddleware
like this:
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allows all origins
allow_credentials=True,
allow_methods=["*"], # Allows all methods
allow_headers=["*"], # Allows all headers
)
Please note that Safari does not support the wildcard *
in some fields.
If wildcard is used in the middleware config, the middleware should explicitly send all values being used.
Source:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers#Browser_compatibility
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods#Browser_compatibility
I seem to be running into the same issue:
app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"])
@app.post("/tokenize/", summary="Process batches of text", response_model=ResponseModel)
def tokenize(query: RequestModel):
# do stuff
but after a POST request from my React front-end, console notifies me:
Access to fetch at 'http://127.0.0.1:8000/tokenize/' from origin 'http://localhost:3000' has been blocked by CORS policy: 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.
@BramVanroy I used from starlette.middleware.cors import CORSMiddleware for workaround and works fine. If this can help you, let me know !
@ScrimForever was it an import
issue in your case?
@ScrimForever was it an
import
issue in your case?
When i use fastapi CORSMiddleware, same as Bram, i receive error when use method POST, so, i change to starlette cors, and works. Just it.
That seems very strange, as fastapi doesn't have a cors implementation - it just exposes starlettes one
See https://github.com/tiangolo/fastapi/blob/master/fastapi/middleware/cors.py
I have the same issue here. any updates, guys?
@tricosmo try importing from Starlette.
from starlette.middleware.cors import CORSMiddleware
@tricosmo try importing from Starlette.
from starlette.middleware.cors import CORSMiddleware
Thanks @ycd , that is what I have. Plus as @Mause mentioned about, it doesn't make sense.
import logging
import uvicorn
from fastapi import APIRouter, Depends, FastAPI
from fastapi_aad_auth import AADAuth, AuthenticationState
from starlette.middleware.cors import CORSMiddleware
auth_provider = AADAuth()
logging.basicConfig(level="DEBUG")
router = APIRouter()
@router.get("/hello")
async def hello_world(
auth_state: AuthenticationState = Depends(auth_provider.api_auth_scheme),
):
print(auth_state)
return {"hello": "world"}
app = FastAPI(
title="fastapi_aad_auth test app",
description="Testapp for Adding Azure Active Directory Authentication for FastAPI",
openapi_url=f"/api/v1/openapi.json",
docs_url="/api/docs",
swagger_ui_init_oauth=auth_provider.api_auth_scheme.init_oauth,
redoc_url="/api/redoc",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:8001", "http://0.0.0.0:8001"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(router)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", debug=True, port=8001, log_level="debug")
Access to fetch at 'https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token' from origin 'http://localhost:8001' has been blocked by CORS policy: 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.
@tricosmo I think you have misunderstood the purpose of cors - you can only configure requests to your website, not from
@Mause Thanks, that is a bit of silly me.
My solution was to move my app.add_middleware(CORSMiddleware, allow_origins=["*"])
to the bottom of all previous app.
configuration. I'm using fastapi_versioning
, and it seems to break middleware when placed after the CORSMiddleware
config.
This is what I'm doing for now. It works for me:
from fastapi import FastAPI
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
origins = [
"http://localhost:5000",
]
middleware = [
Middleware(CORSMiddleware, allow_origins=origins)
]
app = FastAPI(middleware=middleware)
You can check starlette for how to initialize middleware with Starlette's middlewares, and then put that in FastAPI.
Hit the same issue and the prior fixes aren't working. I had to create an APIRoute and wire it into any APIRouter that needs CORS handling. Here's the documentation pertinent to this approach: https://fastapi.tiangolo.com/advanced/custom-request-and-route/ But for some reason I'm getting all OPTIONS disallowed with a 405 method not allowed, so trying to see how to prevent that.
app = FastAPI()
# Handle CORS
class CORSHandler(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def preflight_handler(request: Request) -> Response:
if request.method == 'OPTIONS':
response = Response()
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
else:
response = await original_route_handler(request)
return preflight_handler
router = APIRouter(route_class=CORSHandler)
...
app.include_router(router)
I noticed that OPTIONS requests were being denied until I explicitly added an endpoint handler like @router.options() ...
On further investigation, I saw that starlette allows a kind of path wildcard so any number of routes can be handled by one handler: https://stackoverflow.com/questions/63069190/how-to-capture-arbitrary-paths-at-one-route-in-fastapi
So rather than the above approach, which doesn't work without an explicit options handler, I simply do this now and it seems to work:
app = FastAPI()
# Salt to your taste
ALLOWED_ORIGINS = '*' # or 'foo.com', etc.
# handle CORS preflight requests
@app.options('/{rest_of_path:path}')
async def preflight_handler(request: Request, rest_of_path: str) -> Response:
response = Response()
response.headers['Access-Control-Allow-Origin'] = ALLOWED_ORIGINS
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
return response
# set CORS headers
@app.middleware("http")
async def add_CORS_header(request: Request, call_next):
response = await call_next(request)
response.headers['Access-Control-Allow-Origin'] = ALLOWED_ORIGINS
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
return response
The odd thing is that the suggested CORSMiddleware seemed to work for some of my APIRouters but not others, and applying the middleware at the end of router initializations didn't seem to help.
Thanks @DocSavage this fix worked. But then after reading MDN I realised that my JS client wasn't sending credentials properly. Fixing the client and re-testing I can see that either the DocSavage fix or CORSMiddleware will work as expected.
~I couldn't get CORS headers to work properly until I did what @DocSavage outlined. What's the story with the CORSMiddleware?~ Surprise, surprise: I wasn't send the requests properly.
Same problem. I found it's the order of middlewares cause the bug:
# not working
app.add_middleware(CORSMiddleware,
allow_origins=['*'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'])
app.add_middleware(GZipMiddleware)
# working
app.add_middleware(GZipMiddleware)
app.add_middleware(CORSMiddleware,
allow_origins=['*'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'])
A couple of people (@gocreating) find that changing order of middleware can fix their problem, but I'm wondering if that only works if they aren't using APIRouter approaches (https://fastapi.tiangolo.com/tutorial/bigger-applications/#another-module-with-apirouter). I believe I tried to reorder but couldn't get it to work when using many APIRouters with different modules. One would think that this would work but it didn't seem to work for endpoints in the APIRouter:
app.include_router(foo.router, ...)
app.add_middleware(CORSMiddleware, ...)
Can someone verify the above works for them?
@DocSavage I think it makes more sense to put middlewares before routers. At least that works for me.
OMG The working code yesterday gets CORS error back today... Changing the order of middlewares is useless
Hi, I deployed an app using FastAPI, with CORS set to my frontend IP only but I am still able to call it from another IPs as well.
Hi , if anyone is still having the CORSMiddleware issue,this is what works for me
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware import Middleware
app = FastAPI()
your routes
origins=[*]
app = CORSMiddleware(
app=app,
allow_origins=orgins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
I am still having issues with API routers. It is working with the methods in main.py but router endpoints still fail with CORS issue. Is it working with anyone else? On http://localhost:8000
endpoint, all routes work fine, but this fails when deployed to AWS Lambda -> API Gateway. Can someone help? My main.py
file is below
from fastapi import FastAPI
from mangum import Mangum
from starlette.middleware.cors import CORSMiddleware
from .api.app_v1.api import router as api_router
import json
origins = ["*"]
app = FastAPI() #(middleware=middleware)
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(api_router, prefix="/api/v1")
handler = Mangum(app)
@app.get("/")
async def root():
"""
Base root method
"""
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT'
},
'body': json.dumps('Hello World!')
}
this title is 100% accurate. I'm using a single middleware:
app = FastAPI(middlewares=[
Middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
])
and it's about as lenient as it could possibly be, yet I can't make a single request to the API. I'm no expert in this kind of stuff but perhaps someone could explain why the default behaviour is not being able to hit your API at all?
With CORS middleware enabled, any prefetch OPTIONS requests need to include an extra header to match the method of the route, e.g.
$ curl --location --request OPTIONS 'http://127.0.0.1:8080/a_post_route' \
--header 'Origin: http://example.com'
{"detail":"Method Not Allowed"}
$ curl --location --request OPTIONS 'http://127.0.0.1:8080/a_post_route' \
--header 'Origin: http://example.com' \
--header 'Access-Control-Request-Method: POST'
OK
Sample server logs for those requests:
[08-Apr-21 17:33:38] Application startup complete.
INFO: 127.0.0.1:41126 - "OPTIONS /a_post_route HTTP/1.1" 405 Method Not Allowed
INFO: 127.0.0.1:41138 - "OPTIONS /a_post_route HTTP/1.1" 200 OK
The /a_post_route
is decorated with @app.post(...)
and it does not support any methods other than POST. It seems like the CORS middleware does not generate any additional routes that support OPTIONS methods on those routes (as in adding @app.options(...)
for all routes.
In https://javascript.info/fetch-crossorigin#unsafe-requests it suggests that browsers only add the Access-Control-Request-Method
header for unsafe requests. Since GET
and POST
are safe requests, it does not add the extra header in a prefetch OPTIONS request that is required by the CORS middleware.
On AWS API-Gateway, there needs to be a pass-through on /
and /{proxy+}
for any OPTIONS method to allow FastAPI to handle the CORS requests. After that, API-Gateway is not involved.
Confirmed that @DocSavage comment above in https://github.com/tiangolo/fastapi/issues/1663#issuecomment-763188503 works. The only downside is that requests to routes that do not exist, say GET /missing
, no longer return a 404
but return a 405
instead (a preflight_handler
only supports OPTIONS). To support 404 for missing routes, an additional method to check the request path against the app routes seems to work, e.g.
def check_routes(request: Request):
# Using FastAPI instance
url_list = [
route.path
for route in request.app.routes
if "rest_of_path" not in route.path
]
if request.url.path not in url_list:
return JSONResponse({"detail": "Not Found"}, status.HTTP_404_NOT_FOUND)
# Handle CORS preflight requests
@app.options("/{rest_of_path:path}")
async def preflight_handler(request: Request, rest_of_path: str) -> Response:
response = check_routes(request)
if response:
return response
response = Response(
content="OK",
media_type="text/plain",
headers={
"Access-Control-Allow-Origin": ALLOWED_ORIGINS,
"Access-Control-Allow-Methods": ALLOWED_METHODS,
"Access-Control-Allow-Headers": ALLOWED_HEADERS,
},
)
return response
# Add CORS headers
@app.middleware("http")
async def add_cors_header(request: Request, call_next):
response = check_routes(request)
if response:
return response
response = await call_next(request)
response.headers["Access-Control-Allow-Origin"] = ALLOWED_ORIGINS
response.headers["Access-Control-Allow-Methods"] = ALLOWED_METHODS
response.headers["Access-Control-Allow-Headers"] = ALLOWED_HEADERS
return response
Just a note. Order of middleware matters, If you are having multiple middlewares, the CorsMiddleware should be the last one.