Приложение Flask, размещенное на CherryPy: OPTIONS возвращает 404

У меня есть спокойный API, созданный с использованием Flask и Flask-Restful. Все работает нормально, используя сервер разработки. Все маршруты находятся в Blueprint, хотя конкретный маршрут, с которым мы здесь имеем дело, не является маршрутом Flask-Restful. Это обычный маршрут Flask.

Я также использую Flask-CORS.

Для развертывания все докеризовано, и я использую CherryPy в качестве хоста WSGI. Таким образом, приложение CherryPy размещает приложение Flask в контейнере. Я использую Traefik в качестве обратного прокси-сервера в другом контейнере.

Если я сделаю следующий запрос в Chrome, вставив URL-адрес, запрос GET сработает:

https://api.my-app.new/api/admin/user?_end=10&_order=DESC&_sort=id&_start=0

Однако, если я попытаюсь сделать тот же запрос GET из приложения React, будет сделан предварительный запрос OPTIONS, и он завершится с ошибкой 404. Я проследил его как можно лучше в PyCharm, и проблема, похоже, в следующий код в app.py Flask:

def preprocess_request(self):
    bp = _request_ctx_stack.top.request.blueprint

В принципе чертеж не нашел.

То, что на самом деле отправляется, видно в этом вызове curl:

curl 'https://api.my-app.new/api/admin/user?_end=10&_order=DESC&_sort=id&_start=0' \
     -X OPTIONS -H 'access-control-request-method: GET' -H 'origin: https://admin.my-app.new' \
     -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-US,en;q=0.9' \
     -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36' \
     -H 'accept: */*' -H 'referer: https://admin.my-app.new/' -H 'authority: api.my-app.new' \
     -H 'access-control-request-headers: authorization,content-type' --compressed

И если я распечатаю заголовки, полученные Flask-приложением (в @app.before_request), я получу следующее:

[2018-01-25 04:31:48,438] INFO - X-Forwarded-Server: cb5d56692c6d
Referer: https://admin.my-app.new/
Accept-Language: en-US,en;q=0.9
Origin: https://admin.my-app.new
X-Real-Ip: 172.19.0.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Access-Control-Request-Headers: authorization,content-type
X-Forwarded-Proto: https
Host: api.my-app.new
Accept: */*
Access-Control-Request-Method: GET
X-Forwarded-Host: api.my-app.new
X-Forwarded-For: 172.19.0.1
X-Forwarded-Port: 443
Accept-Encoding: gzip, deflate, br

[2018-01-25 04:31:48,440] INFO - Error 404:/api/admin/user?_end=10&_order=DESC&_sort=id&_start=0: 404 Not Found: The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again. ("/app/app/routes.py:87")

Теперь, если я сделаю тот же запрос с приложением, работающим без обратного прокси-сервера traefik, он сработает. Единственное, что отличается в запросе curl, это то, что я использую http вместо https.

Вот заголовки, которые Flask получает в этом случае:

[2018-01-24 21:43:43,199] INFO - Referer: https://admin.my-app.new/
Origin: https://admin.my-app.new
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Authority: api.my-app.new
Access-Control-Request-Headers: authorization,content-type
Host: api.my-app.new:8000
Accept: */*
Access-Control-Request-Method: GET
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br

[24/Jan/2018 21:43:43] "OPTIONS /api/admin/user?_end=10&_order=DESC&_sort=id&_start=0 HTTP/1.1" 200 -

Я полагаю, что что-то не так с заголовками или что-то, что сбивает с толку Flask. Я видел еще один пост от 2014 года, в котором упоминалось о необходимости SERVER_NAME, но это не помогло.

Наконец, я изначально использовал NGinx в качестве резервного прокси, и все заработало. Одной из вещей, с которыми было трудно работать с NGinx, были перенаправления и запросы OPTION. Я заставил его работать после безумной погони за различными сообщениями в блогах, но когда я оглядываюсь назад, когда исправляю это, я замечаю любопытную вещь: я написал скрипт в nginx.conf, чтобы автоматически возвращать 200 для всех запросов OPTIONS!

Любая идея, почему запросы OPTION терпят неудачу?


person markand    schedule 25.01.2018    source источник
comment
Это ошибка. Не стесняйтесь внести свой вклад в исправление этого. Понижение должно помочь.   -  person webknjaz    schedule 26.01.2018
comment
Спасибо. Я нашел проблему CherryPy вчера вечером.   -  person markand    schedule 26.01.2018


Ответы (1)


Как отмечает webKnjaZ: «Это ошибка».

Покопавшись глубже, я обнаружил, что проблема заключалась в том, что Flask получал другой URL-адрес для запроса GET, чем для запроса OPTIONS, и что если удалить CherryPy, проблема исчезла. Это привело меня к этой проблеме CherryPy, которая точно описывает мою ситуацию.

https://github.com/cherrypy/cherrypy/issues/1662

Автор отметил, что ошибка появилась при переходе с CherryPy 11 на 12 (у меня была 13.x), поэтому я попытался перейти на 11.0.0, и это исправило ее.

person markand    schedule 26.01.2018
comment
Я работаю над исправлением и, вероятно, сделаю рефакторинг в Cheroot. Мне просто нужно решить, как лучше поступить. - person webknjaz; 28.01.2018
comment
Я подписан на баг, так что буду следить. Однако похоже, что старое поведение было правильным. - person markand; 28.01.2018