https://fastapi.tiangolo.com/tutorial/middleware/
middleware는 handler의 앞, 뒤로 실행되는 handler로 다음과 같이 실행이 가능하다.
client --request--> Middleware --request--> Handler --response--> Middleware --resonse--> client
물론 handler 앞 뒤로 모두 middleware가 동작하게 할 수 있고, request받을 때만 동작, response받을 때만 동작하게도 할 수 있다.
사실 Middleware
는 Depends
로 해결이 가능하며, 실제로 fastapi에서는 middleware를 쓰는 것보다 Depends
를 쓰는 것을 권장한다. 왜냐하면 fastapi에서 제공하는 middleware
는 매우 빈약하기 때문이다.
import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
middleware를 만들기 위해서는 @app.middleware('http')
decorator를 function위에 만들어주면 된다. middleware
decorator를 사용하는 함수에는 2가지 인자가 들어오는데, 하나는 client로부터 오는 request
parameter이고 다른 하나는 call_next
로 middleware다음에 실행될 handler이다. 따라서 request
를 call_next
에 전달해주어야한다.
또한, middleware
함수는 call_next
에게서 response
를 받고, 원하는 처리 이후에 client에게 Response
를 보낼 수 있다.
위의 code는 middleware로 감싸진 handler인 call_next
가 실행되는 시간을 측정하고 마지막에 응답을 전송할 때 header를 추가하고 전송하는 것이다.
중요한게 있는데 fastapi에서는 http
로 요청이 오는 모든 handler에 대해서 위의 middleware가 적용된다는 것이다. custom하게 설정하는 방법이 있긴하지만 개별적으로 middleware를 각각 적용하지 못한다. 따라서, middleware보다는 동작이 비슷하지만 더 편하고 다양하게 사용할 수 있는 Dependency를 사용하는 것을 추천한다.
CORS(cross-origin resource sharing)은 frontend와 backend의 'origin'이 서로 다른 경우에 발생하는 상황을 말한다. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Origin
이란 protocol(http, https) + domain(myapp.com, localhost.com) + port(80, 443)의 조합을 말한다. 따라서 같은 localhost
라도 http
냐 https
에 따라서도 다르고 port번호에 따라서도 달라진다.
http://localhost:8080
에 동작하고 있는 fronted app이 있다고 하자. 해당 app은 backend인 http://localhost
에 요청을 보내려고 한다. browser에서는 CORS
를 위해서 frontend의 request보다 먼저 HTTP OPIONS
요청을 backend로 먼저 보내는데, 여기서 통신에 성공하기 위한 적절한 header와 규약이 있어야, frontend의 request가 backend로 전달된다.
이러한 동작을 CORS preflight requests
라고 한다. Origin
과 Access-Control-Request-Method
header를 가진 OPTIONS
requests를 전달한다. fastapi
에서는 middleware
module을 제공하여 preflight요청이 오면 적절한 CORS header와 함께 응답을 전송한다.
이를 위해서 backend에서는 allowed origins
list를 가져야하고, 해당 list에 frontend주소인 http://localhodt:8080
가 명시되어있어야 한다.
fastapi에서는 CORSMiddleware
를 사용해서 CORS문제를 해결할 수 있다. CORSMiddleware
에 허용할 origin list들과 어떤 http method, header를 허용할 지 지정할 수 있다.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def main():
return {"message": "Hello World"}
allow_origins
: origin의 list로 cross-origin 요청들을 허용한다. 가령 ['*']
으로 적으면 모든 orign을 허용하고 ["https://example.org"]
면 https://example.org
에서만 오는 request만 받는다.allow_origin_regex
: regex string으로 origin을 표현한 것이다. 'https://.*\.example\.org'
와 같은 것들이 가능하다.allow_methods
: cross-origin request에 대해서 허용되는 HTTP method들을 지정한다. 기본적으로 ['GET]
이 default이고 ['*']
으로 적으면 모두 가능하다.allow_headers
: cross-origin request를 위해 지원되어야할 HTTP request header의 list이다. default로 []
이고 모든 header를 받기 위해서 ['*']
도 가능하다. simple CORS request에 대해서는 Accept
, Accept-Language
, Content-Language
, Content-Type
이 항상 허용된다. allow_credentials
: cross-orign request를 위해 지원되어야할 cookie들을 지정한다. 기본적으로 False
이다. 또한 credential이 허용된다면 allow_origins
가 ['*']
로 설정될 수가 없고, 반드시 origin을 명시해야한다.expose_headers
: 브라우저에 접근 가능하도록 만들어야하는 response header를 지정한다. default는 []
이다. max_age
: 브라우저에서 CORS 응답들을 캐싱하기 위한 seconds를 지정한다. default로 600
이다. 정리하면 다음과 같다.
| ---preflight request(OPTIONS) ---> |
client | <-- CORS response with header(origin) | server
| ---actual request(GET, POST ...) ---> |
| <-- actual response --- |
우리가 설정한 middleware는 preflight
가 오고 CORS response를 보낼 때 필요한 origin, credential 여부 등의 header를 설정하고 응답을 보내는 것이다.