프로그래밍/Python

flask RESTful API의 CORS 설정

채윤아빠 2021. 9. 9. 21:32
728x90
반응형

개요

파이선 Flask를 이용하여 간단하게 RESTful API 만들어서 유용하게 이용하고 있었습니다.

외부에서 작성된 RESTful API를 이용하려고 하니, CORS(Cross Origin Resource Sharing) 문제가 발생하여 이를 해결하는 과정을 정리해 둡니다.

CORS (Cross Origin Resource Sharing) 란?

우리말로 번역하면 "교차 출처 리소스 공유"로 브라우저 등에서 서버가 다른 출처의 리소스(API 등)를 요청을 허용할 수 있도록 하는 웹 서버의 정책이라고 볼 수 있습니다.
외부 다른 도메인 등에서의 요청을 허용하지 않으면, CORS 정책 위반으로 요청에 대한 처리가 실패하게 됩니다.

관련된 요청 헤더는 다음과 같습니다.

Origin
Access-Control-Request-Method
Access-Control-Request-Headers

브라우저 등 클라이언트 입장에서는 주로 다음과 같은 서버의 응답 헤더에 영향을 받습니다.

Access-Control-Allow-Origin: *

특정 리소스에 대한 요청에 대해 서버에서 위와 같은 응답 헤더가 포함되어야 요청이 정상적으로 처리됩니다.

그 밖에 영향받는 응답 헤더는 다음과 같습니다.

Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Allow-Methods
Access-Control-Allow-Headers

flask-cors의 설치

Flask에서 CORS를 적용하기 위하여 "flask-cors" 패키지를 다음과 같이 설치합니다.

$ pip install -U flask-cors

예제 코드

CORS를 모든 경로 (route)에 적용할 경우는 다음과 같이 설정합니다.

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
    return "Hello, cross-origin-world!"

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80, debug=True)

특정 Blueprint에 CORS 적용하기

from flask import Flask, Blueprint, jsonify
from flask_cors import CORS

app = Flask(__name__)

api_v1 = Blueprint('API_v1', __name__)
CORS(api_v1)

@app.route("/")
def helloWorld():
    return "Hello, world!"

@api_v1.route("/api/v1/users")
def list_users():
    return jsonify(user="hbesthee@naver.com")

app.register_blueprint(api_v1) # enable CORS on the API_v1 blue print

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80, debug=True)

CORS extension을 이용하여 특정 주소 패턴에만 적용하기

CORS(app, resources=r'/api/*')

@app.route("/")
def helloWorld():
    """
        Since the path '/' does not match the regular expression r'/api/*',
        this route does not have CORS headers set.
    """
    return 'hello world'

@app.route("/api/v1/users/")
def list_users():
    """
        Since the path matches the regular expression r'/api/*', this resource
        automatically has CORS headers set
    """
    return jsonify(user="hbesthee@naver.com")

"cross_origin" 데코레이터를 이용하는 방법

@app.route("/")
@cross_origin()
def helloWorld():
    """
        This view has CORS enabled for all domains, representing the simplest
        configuration of view-based decoration.
    """
    return 'hello world'

@app.route("/api/v1/users/create", methods=['GET', 'POST'])
@cross_origin(allow_headers=['Content-Type'])
def cross_origin_json_post():
    """
        This view has CORS enabled for all domains, and allows browsers
        to send the Content-Type header, allowing cross domain AJAX POST
        requests.
    """
    return jsonify(user="hbesthee@naver.com")

CORS 적용된 API 호출 예시

CORS가 적용된 API를 curl로 호출하면 다음과 같이 헤더에 "Access-Control-Allow-Origin: *"가 포함된 것을 확인하실 수 있습니다.

$ curl -sS -v localhost/api/v1/users
{
  "user": "hbesthee@naver.com"
}
*   Trying ::1:80...
* TCP_NODELAY set
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /api/v1/users HTTP/1.1
> Host: localhost
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Content-Length: 35
< Access-Control-Allow-Origin: *
< Server: Werkzeug/1.0.1 Python/3.8.3
< Date: Fri, 10 Sep 2021 02:17:04 GMT
<
{ [35 bytes data]
* Closing connection 0

참고자료