CORS๋ฅผ ์ง์ํ๋ ๊ฒ์ด ์ข์ ๊ฒ์ ๋๋ค [1][2]
์ ์ ํ์ผ๋ ์ง์ํด์ผ ํฉ๋๋ค.
Werkzeug๋ WSGI ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฐํํ๋ ์๋ต์ ํค๋๋ฅผ ์ค์ ํ ์ ์๋ค๋ ์ ์์ ์ด๋ฏธ CORS๋ฅผ ์ง์ํฉ๋๋ค. Werkzeug ๋ด์์ "CORS ์ง์"์ ์ํด ๋ฌด์์ ํ ์ ์๊ฑฐ๋ ํด์ผ ํ๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ์์ธํ ์ค๋ช ํด ์ฃผ์๊ฒ ์ต๋๊น?
์ ๋ OP๊ฐ ์๋์ง๋ง CORS๋ ๊ฑฐ์ ์์ฉ๊ตฌ ํค๋ ์ฃผ์ ์ด๋ฏ๋ก ์ค์ /๋งค๊ฐ๋ณ์๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ด ์ค ๋ํซ ์ ๋ผ์ธ์ ๋ฐ๋ผ ๋ญ๊ฐ๊ฐ ์์ง๋ง ์ฌ๊ธฐ์๋ ํนํ OPTIONS๊ฐ ์์ผ๋ฏ๋ก preflight๊ฐ ๋ฌธ์ ๊ฐ ๋ ๊ฒ์ ๋๋ค ...
if 'cors' in config and config['cors'] is True:
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Methods',
'GET,PUT,POST,DELETE,PATCH')
response.headers.add('Access-Control-Allow-Headers',
'Content-Type, Authorization')
...ํ์ด์ฌ ๋งํธ๋ผ "๋ฐฐํฐ๋ฆฌ ํฌํจ"์ ์๊ณ ์์ต๋๋ค... ;)
๊ทธ๋ฌ๋ CORS๋ ํนํ ์ฟ ํค์ ๋ฌผ๊ฑด์ ์ฌ์ฉํ ๋ *
ํ์ฉํ๋ ๊ฒ ์ด์์
๋๋ค. ๊ฐ์ธ์ ์ผ๋ก ์ ๋ 20 LoC WSGI ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ์ฌ CORS ํค๋๋ฅผ ์ถ๊ฐํ๊ณ ๋นํ ์ ์์ฒญ์ ์๋ตํฉ๋๋ค. ๊ทธ๋ ๊ฒ ์ด๋ ต์ง ์์ต๋๋ค.
@posativ - ์์ ์ ๊ณต์ ํ๊ณ
๋งค์ฐ ์ ํ๋ ํํ๋ก:
from werkzeug.datastructures import Headers
class CORSMiddleware(object):
"""Add Cross-origin resource sharing headers to every request."""
def __init__(self, app, origin):
self.app = app
self.origin = origin
def __call__(self, environ, start_response):
def add_cors_headers(status, headers, exc_info=None):
headers = Headers(headers)
headers.add("Access-Control-Allow-Origin", self.origin)
headers.add("Access-Control-Allow-Headers", "Origin, ...")
headers.add("Access-Control-Allow-Credentials", "true")
headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, etc")
headers.add("Access-Control-Expose-Headers", "...")
return start_response(status, headers.to_list(), exc_info)
if environ.get("REQUEST_METHOD") == "OPTIONS":
add_cors_headers("200 Ok", [("Content-Type", "text/plain")])
return [b'200 Ok']
return self.app(environ, add_cors_headers)
# usage
app = CORSMiddleware(make_app(...), "http://example.org/")
์ฌ์ฉ์ ์ ์ ํค๋, ์๊ฒฉ ์ฆ๋ช , ์์ผ๋ ์นด๋๋ฅผ ํฌํจํ๋๋ก ์ด ํด๋์ค๋ฅผ ํ์ฅํ ์ ์์ต๋๋ค.
@posativ - ๊ฐ์ฌํฉ๋๋ค. ๋ค์ ์ฃผ ์ด์ ํด๋น ํ๋ก์ ํธ์ ๋ํ ์์ ์ ์ฌ๊ฐํ ๋ ์๋ํด ๋ณด๊ฒ ์ต๋๋ค. :)
์ด๊ฒ์ ๋ถํ๋ ค์ผ ํฉ๋๋ค.
์๋์ ์ง๋ฌธ http://stackoverflow.com/questions/19382431/set-header-on-werkzeug-exception
๊ด๋ จ ๋ผ์ธ: https://github.com/pallets/werkzeug/blob/master/werkzeug/debug/__init__.py#L297
start_response('500 INTERNAL SERVER ERROR',
๋ Access-Control-Allow-Origin: *
์ ๊ฐ์ CORS ํค๋๋ฅผ ์ถ๊ฐํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ญ๋๋ค. ํ์ง๋ง ํฅ๋ฏธ๋กญ๊ฒ๋ ์ด๋ฏธ X-XSS-Protection: 0
๊ฐ ์์ต๋๋ค.
Flask-CORS๋ฅผ ํ์ฑํํ๊ณ Chrome์์ ๊น๋ค๋ก์ด ์๊ฒฉ Ajax API ํธ์ถ์ ๋๋ฒ๊น ํ๊ณ ์์ต๋๋ค. Flask์ ์์ธ๊ฐ ์์ ๋ CORS๋ก ์ธํด ๋๋ฒ๊ทธ ๋ด์ฉ์ ๋ณผ ์ ์์ต๋๋ค. ๋๋ฒ๊ทธ ๋ชจ๋ธ์ ์ด ํค๋๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๊น?
๋๋ฒ๊ฑฐ์์ CORS ํค๋๋ฅผ ์ค์ ํ๋ ค๋ ์ด์ ๋ ๋ฌด์์ ๋๊น? ๊ทธ๋ฆฌ๊ณ ์ ๋ํ ๋ฏธ๋ค์จ์ด๋ก ๊ทธ๋ ๊ฒ ํ ์ ์์ต๋๊น?
๋ํ werkzeug์ ๋ชจ๋ ๋ฏธ๋ค์จ์ด๋ฅผ ์ถ๊ฐํ ํ์๊ฐ ์๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ซ๊ณ ์ถ์ต๋๋ค.
@unititaker
๋๋ฒ๊ฑฐ์์ CORS ํค๋๋ฅผ ์ค์ ํ๋ ค๋ ์ด์ ๋ ๋ฌด์์ ๋๊น?
๊ต์ฐจ ๋๋ฉ์ธ AJAX ์์ฒญ์ ์ํด ํธ์ถ๋๋ ๊น๋ค๋ก์ด ์๋๋ฆฌ์ค์ด๊ธฐ ๋๋ฌธ์ ์ผ๋ จ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ ํ์๋ง Chrome DevTool์์ ๋๋ฒ๊ทธํ ์ ์์ต๋๋ค. CORS ํค๋๊ฐ ์์ผ๋ฉด Chrome DevTool์ ์ฝํ ์ธ ๋ฅผ ์ ํ ํ์ํ์ง ์์ต๋๋ค. ๋๋ ์๋ฌด๊ฒ๋ ๋ณผ ์ ์์ต๋๋ค. 500 ์๋ฒ ์ค๋ฅ๊ฐ ์๋ ๋น ํ์ด์ง๋ง ์์ํ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ ๋ํ ๋ฏธ๋ค์จ์ด๋ก ๊ทธ๋ ๊ฒ ํ ์ ์์ต๋๊น?
๋ํ๋ ๋ฏธ๋ค์จ์ด๋ NORMAL (์ ์ ํ๊ฒ ๋ฐํ๋ Flask ์์ฉ ํ๋ก๊ทธ๋จ) ํ์ด์ง์์๋ง ์๋ํ์ง๋ง Werkzeug ์์ธ ๋๋ฒ๊ฑฐ๊ฐ ๋ฐ์ํ๋ฉด ์ ํ ์๋ํ์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
https://github.com/corydolphin/flask-cors/issues/67์์ ์ธ๊ธํ๋ "๋ํ ๋ฏธ๋ค์จ์ด"๊ฐ ์์ต๋๋ค. ๋ฎ์ ์์ค์ Werkzeug ํญ๋ชฉ์ด ์กฐ์ ๋์ง ์๋ ํ ์ด์ ๋ํด ๋ง์ ๊ฒ์ ํ ์ ์์ต๋๋ค.
@untitaker ๊ฐ ๋ด๊ฐ ๋งํฌํ ์์ค ์ฝ๋๋ฅผ ์ดํด๋ณด์ง ์์๋ค๊ณ ๊ฐ์ ํฉ๋๊น? ๋ค์์ ์คํฌ๋ฆฐ์ท์ ๋๋ค.
@lambdaq ์ง๊ธ ๋น์ ์ ์ฌ์ ๊ถ๊ฒ ๊ตด๊ณ ์์ต๋๋ค
๋๋ฒ๊ทธ ๋ํ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ํํ๋ ๋ชจ๋ ๋ฏธ๋ค์จ์ด๋ start_response ๋์์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค(๋๋ฒ๊ฑฐ์ ๊ฒฝ์ฐ์๋). ๋ฐ๋ผ์ ๋ถ์ฌ๋ฃ๊ณ ๋งํฌํ ์ฝ๋๋ ์์ ํ ๊ด๋ จ์ด ์๋ ๊ฒ์ฒ๋ผ ๋ณด์
๋๋ค.
cors ๋ฏธ๋ค์จ์ด๋ฅผ ๊ฐ์ธ๋ ๊ฒ์ ๊ทธ๊ฒ์ ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์
๋ค๋ฅธ ๊ด์ /์ดํด๋ก ์ธํด ์ ํํ ๋ฌธ์ ๊ฐ ๋๋ฝ๋์์ ์ ์์ต๋๋ค. ๊นจ์ง/๋ถ๊ฐ๋ฅํ ๊ฒ์ฒ๋ผ ๋ณด์ด๋ ์ ํํ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ๊ฐ๋ตํ๊ฒ ์ค๋ช ํ์ญ์์ค.
app.run์ ํธ์ถํ ๋ ๋๋ฒ๊ฑฐ ๋ฏธ๋ค์จ์ด๊ฐ ์ ์ฉ๋๊ธฐ ๋๋ฌธ์ ์ํฉ์ด ์ฝ๊ฐ ๊น๋ค๋ก์์ง๋๋ค. ๊ทธ๋๋ CORS์ ๋๋ฒ๊ฑฐ ๋ฏธ๋ค์จ์ด๋ฅผ ๋ชจ๋ ์๋์ผ๋ก ์ ์ฉํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค. IMO๋ ์ด๋ฌํ ์ฃ์ง ์ผ์ด์ค์ ์ ํฉํ์ง๋ง ๋ณด์์ ์์์ํค์ง ์๋ ๋ฐฉ์์ผ๋ก ๋๋ฒ๊ฑฐ ๋ฏธ๋ค์จ์ด๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์์ด ์์ผ๋ฉด ์ ์ํ์ญ์์ค. ํ์ง๋ง ์ฌ์ด ๋ณํ๋ ๋ชจ๋ฆ ๋๋ค.
๋ํ Werkzeug์ ์ฝ๋๋ฅผ ์ ์๊ณ ์์ต๋๋ค. ๋ด๊ฐ ๊ทธ๊ฒ์ ์ ์งํ๋ ์ด์ ๊ฐ์์?
์ฃ์กํฉ๋๋ค. ์ ์ธ์ด์ ๋ํด ์ฌ๊ณผ๋๋ฆฝ๋๋ค. Werkzeug๋ ๋ฉ์ง ํ๋ก์ ํธ์ ๋๋ค.
์ด start_response()
ํ์ ์์ญ์ด ํธ์งํ๊ณ ๋ด ๊ฐ๋ฐ ํ๊ฒฝ์ CORS ํค๋ ๋ฌด์ฐจ๋ณ ๋์
์ ์ถ๊ฐํด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ํธ์ด ๋ฏธ๋ค์จ์ด๋ณด๋ค ๋ ํธ๋ฆฌํฉ๋๋ค.
๋ํ 500 ์๋ต์ด ๋ฐํ๋๋ ๊ฒฝ์ฐ ๋๋ฒ๊ฑฐ๋ก ๋ฆฌ๋๋ ์ ํ๋ ๋์ ์ถ๊ฐ JS ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ ๊ณ ๋ คํ ์ ์์ต๋๋ค.
@untitaker JS ์ฝ๋๋ ์๋ฌด ๊ฒ๋ ํ ์ ์์ต๋๋ค. 500 ์ค๋ฅ๊ฐ ์์ผ๋ฉด http ์ํ ์ฝ๋๊ฐ 500์ด๊ณ ๋๋ฒ๊ฑฐ ํ์ด์ง์ ์ญ์ถ์ ๋ง
๋ํ js ์ฝ๋๋ ๋ค๋ฅธ ํ์์ ๊ฐ๋ฐํ ์๋ง์ ํญ๋ชฉ์ผ๋ก ์ปดํ์ผ๋ reactjs์ด๋ฉฐ npm run dev
๊น์ง 300์ด ์ด์ ๊ฑธ๋ฆฝ๋๋ค. ์ฝ๊ฐ์ ๋๋ฒ๊ทธ ๋ผ์ธ์ ์์ ํ๊ฑฐ๋ ์ถ๊ฐํ๊ธฐ๊ฐ ์ฝ์ง ์์ต๋๋ค.
๋๋ #1699๊ฐ ์ด๊ฒ์ ํด๊ฒฐํ๋ค๊ณ ์๊ฐํ๋ค. Werkzeug๋ #1699์ ์๋ ๊ฒ๋ณด๋ค ๋ ๋ง์ ์์ ์ ์ํํ ์ ์์ง๋ง (์บ์ ์ ์ด์ ๊ฐ์) ์ ๊ทผ์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๊ณ ์ฌ๊ธฐ์ ๋น๋๋ ๋๊ตฌ(์: Flask-CORS ๋๋ Quart-CORS)๊ฐ CORS ๋ ผ๋ฆฌ๋ฅผ ์ํํ๋๋ก ํ์ฉํด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๋ํ werkzeug์ ๋ชจ๋ ๋ฏธ๋ค์จ์ด๋ฅผ ์ถ๊ฐํ ํ์๊ฐ ์๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ซ๊ณ ์ถ์ต๋๋ค.