Django+Angular CORS не работает с POST

Мое приложение Angular4 (работает на http://127.0.0.1:4200 сервер разработки) должен иметь доступ к бэкэнду REST django в сети. Бэкэнд находится под моим контролем и доступен только через HTTPS (работает Apache, который туннелирует запрос на сервер gunicorn, работающий на внутренний порт). Допустим, это https://example.com/, По историческим причинам вход пользователя в систему осуществляется с помощью сеансов, потому что я хочу, чтобы пользователи могли также использовать интерфейс администратора Django после входа в систему. Рабочий процесс выглядит следующим образом:

  • Пользователи открываются http://127.0.0.1:4200Я выполняю запрос GET https://example.com/REST/is_logged_in который возвращает 403, когда пользователь еще не вошел в систему через сеансы, 200 в противном случае. В первом случае пользователь перенаправляется на https://example.com/login/, созданный механизмом шаблонов Django, позволяющий пользователю войти в систему. После входа пользователь перенаправляется на http://127.0.0.1:4200
  • При нажатии на какую-либо кнопку в моем Angular UI выполняется запрос POST. Этот пост-запрос завершается с ошибкой 403, даже несмотря на то, что в предварительном запросе OPTIONS явно указывается POST как разрешенные действия.

Вот моя конфигурация CORS в Django:

NG_APP_ABSOLUTE_URL = 'http://127.0.0.1:4200'
# adapt Django's to Angular's presumed XSRF cookie/header names
CSRF_COOKIE_NAME = "XSRF-TOKEN"
CSRF_HEADER_NAME = "HTTP_X_XSRF_TOKEN"
CORS_ORIGIN_WHITELIST = (
    urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CSRF_TRUSTED_ORIGINS = (
    urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CORS_ALLOW_HEADERS = default_headers + (
    'x-xsrf-token',
)
CORS_ALLOW_CREDENTIALS = True

Вот что сообщает Chrome для (успешного, 200) первого запроса REST GET, чтобы проверить, вошел ли пользователь в систему (после того, как он успешно это сделал) в ответе:

Access-Control-Allow-Credentials:true Access-Control-Allow-Origin:http://127.0.0.1:4200 Allow:GET, HEAD, OPTIONS Connection:close Content-Type:application/json Date:Wed, 26 Apr 2017 15:09:26 GMT Server:gunicorn/19.6.0 Set-Cookie:XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:09:26 GMT; Max-Age=31449600; Path=/ Transfer-Encoding:chunked Vary:Accept,Cookie,Origin X-Frame-Options:SAMEORIGIN

Соответствующий запрос имел это:

Cookie:sessionid=...; XSRF-TOKEN=... Host:example.com Origin:http://127.0.0.1:4200 Referer:http://127.0.0.1:4200/

Теперь к актуальной проблеме:

Предполетный запрос:

Request URL:https://example.com/REST/change_user_data/
Request Method:OPTIONS
Status Code:200 OK
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account

Предполетный ответ:

Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with, x-xsrf-token
Access-Control-Allow-Methods:DELETE, GET, OPTIONS, PATCH, POST, PUT
Access-Control-Allow-Origin:http://127.0.0.1:4200
Access-Control-Max-Age:86400
Connection:close
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Wed, 26 Apr 2017 15:36:56 GMT
Server:gunicorn/19.6.0
Vary:Origin
X-Frame-Options:SAMEORIGIN

Теперь мой неудачный (403) POST-запрос:

Accept:application/json
Accept-Encoding:gzip, deflate, br
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Content-Length:60
Content-Type:application/json
Cookie:sessionid=...; XSRF-TOKEN=...
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account

Заголовки ответа:

HTTP/1.1 403 Forbidden
Date: Wed, 26 Apr 2017 15:36:56 GMT
Server: gunicorn/19.6.0
Vary: Accept,Cookie,Origin
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Access-Control-Allow-Credentials: true
Allow: POST, OPTIONS
Access-Control-Allow-Origin: http://127.0.0.1:4200
Set-Cookie: XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:36:56 GMT; Max-Age=31449600; Path=/
Connection: close
Transfer-Encoding: chunked

Почему этот запрос не работает? Это имеет мало смысла для меня!

С наилучшими пожеланиями!

1 ответ

Решение

У меня была такая же проблема, когда я пытался отправить запрос POST в Django (порт 8000) из моего Angular CLI (порт 4200). Я подумал, что это проблема Django, поэтому я установил пакет cors, однако "проблема" связана с браузером (на самом деле это не проблема, это проблема безопасности, см. Здесь). В любом случае, я решил проблему с добавлением прокси-правила для моего Angular CLI следующим образом:

  • Во-первых, вместо того, чтобы отправлять мои запросы по http://localhost:8000/api/..., отправляйте их в / api / (то есть на мой сервер ng, работающий на порту 4200).
  • Затем я добавил в свой проект Angular файл с именем "proxy.conf.json" со следующим содержимым:
{
  "/api": {
    "target": "http://localhost:8000",
    "secure": false
  }
}
  • Наконец, запустите ваш сервер ng с флагом "--proxy-config":ng serve --watch --proxy-config proxy.conf.json

Все запросы API будут отправлены на порт 4200, и Angular внутренне перенаправит их на Django, избегая проблемы CORS. Обратите внимание, что это действительно только для разработки и не будет использоваться, когда вы создаете код приложения и добавляете его как статический код вашего сервера Django.

Наконец, с этим решением мне больше не понадобился модуль python для cors, чтобы вы могли его удалить.

Другие вопросы по тегам