Если вы когда-либо работали с вызовом AJAX, вы, вероятно, знакомы со следующей ошибкой, отображаемой в консоли браузера:

Если вы видите это сообщение, это означает, что ответ не прошел, но вы по-прежнему можете увидеть возвращенные данные, если перейдете на вкладку «Сеть». В чем тут идея?

Совместное использование ресурсов между источниками (CORS)

Наблюдаемое вами поведение является результатом реализации CORS в браузерах.

До того, как CORS стал стандартизированным, из соображений безопасности не было возможности вызвать конечную точку API в другом домене. Это было (и до некоторой степени все еще блокируется) политикой одного и того же происхождения.

CORS - это механизм, который направлен на то, чтобы разрешать запросы, сделанные от вашего имени, и в то же время блокировать некоторые запросы, сделанные мошенническим JS, и запускается всякий раз, когда вы делаете HTTP-запрос на:

Этот механизм предотвращает злоумышленников, которые размещают сценарии на различных веб-сайтах (например, в рекламе, отображаемой через Google Рекламу), чтобы сделать AJAX-вызов на www.yourbank.com и в случае, если вы вошли в систему для совершения транзакции, используя * ваши * учетные данные.

Если сервер не отвечает определенными заголовками на «простой» GET или POST запрос - он все равно будет отправлен, данные все равно будут получены, но браузер не позволит JavaScript получить доступ к ответу.

Если ваш браузер пытается сделать «непростой» запрос (например, запрос, который включает файлы cookie или который Content-type отличается от application/x-ww-form-urlencoded, multipart/form-data или text-plain), будет использован механизм под названием предварительная проверка и OPTIONS запрос будет отправлен на сервер.

Типичным примером «непростого» запроса является добавление файлов cookie или настраиваемых заголовков - если ваш браузер отправляет такой запрос, а сервер не отвечает должным образом, будет выполнен только предварительный вызов (без дополнительных заголовков), но фактический HTTP-запрос Браузер, предназначенный для создания, не будет отправлен.

Контроль доступа-Разрешить-Что?

CORS использует несколько HTTP-заголовков - как в запросе, так и в ответе, - но вы должны понимать следующие заголовки, чтобы продолжить работу:

Доступ-Контроль-Разрешить-Происхождение

Этот заголовок должен возвращаться сервером и указывать, каким доменам клиентов разрешен доступ к его ресурсам. Значение может быть:

  • * - разрешить любой домен
  • полное доменное имя (например, https://example.com)

Если вам требуется, чтобы клиент передавал заголовки аутентификации (например, файлы cookie), значение не может быть * - это должен быть полностью определенный домен!

Доступ-Контроль-Разрешить-Учетные данные

Этот заголовок должен присутствовать в ответе только в том случае, если ваш сервер поддерживает аутентификацию с помощью файлов cookie. Единственное допустимое значение для этого случая - true.

Доступ-Контроль-Разрешить-Заголовки

Предоставляет список значений заголовка запроса, разделенных запятыми, которые сервер готов поддерживать. Если вы используете настраиваемые заголовки (например, x-authentication-token, вам нужно вернуть его в этом ответе заголовка ACA на вызов OPTIONS, в противном случае запрос будет заблокирован.

Access-Control-Expose-Заголовки

Точно так же этот ответ должен содержать список заголовков, которые будут присутствовать в фактическом ответе на вызов и должны быть доступны клиенту. Все остальные заголовки будут ограничены.

Доступ-Контроль-Разрешить-Методы

Разделенный запятыми список глаголов типа HTTP-запроса (например, GET, POST), которые сервер готов поддерживать.

Источник

Этот заголовок является частью запроса, который делает клиент, и будет содержать домен, из которого запускается приложение. По соображениям безопасности браузеры не позволят вам перезаписать это значение.

Как исправить «ошибку» CORS?

Вы должны понимать, что поведение CORS не является ошибкой - это механизм, который работает должным образом, чтобы защитить ваших пользователей, вас или сайт, на который вы звоните.

Иногда отсутствие правильных заголовков является результатом неправильной реализации на стороне клиента (например, отсутствие данных авторизации, таких как ключ API).

Есть несколько способов «исправить ошибку» в зависимости от ситуации, с которой вы столкнулись:

A - Я разрабатываю интерфейс и контролирую или знаю человека, разрабатывающего серверную часть

Это лучший сценарий - у вас должна быть возможность реализовать правильный ответ CORS на сервере, который вы вызываете. Если API использует express для узла, вы можете использовать простой пакет cors. Если вы хотите, чтобы ваш сайт был надежно защищен, рассмотрите возможность использования белого списка для заголовка Access-Control-Allow-Origin.

B - Я разрабатываю интерфейс, но сейчас не могу его контролировать, мне нужно временное решение

Это второй лучший сценарий, потому что он всего лишь A, но с некоторыми ограничениями по времени. Чтобы временно решить эту проблему, вы можете заставить браузер игнорировать механизм CORS - например, использовать расширение ACAO для Chrome или полностью отключить его, запустив Chrome со следующими флагами:

chrome --disable-web-security --user-data-dir

ВАЖНО, помните, что это отключит механизм для каждого веб-сайта на время всего сеанса браузера. Используйте с осторожностью.

Другой альтернативой здесь является использование devServer.proxy (при условии, что вы используете веб-пакет для обслуживания своего приложения) или использование решения CORS-as-a-service, такого как https://cors-anywhere.herokuapp.com/

C - Я разрабатываю интерфейс, не контролирую серверную часть и никогда не буду

Хорошо, теперь все усложняется. Сначала вам, вероятно, следует установить почему сервер не отправляет правильные заголовки.

Может быть, они не разрешают сторонним приложениям получать доступ к своему API? Может быть, их API предназначен только для использования серверными приложениями, а не браузерами? Может быть, вам следует отправить своего рода токен авторизации в URL-адрес?

Если вы по-прежнему считаете, что у вас должен быть доступ к данным через браузер, вам придется написать свой собственный прокси-сервер, который будет находиться между приложением браузера и API, аналогично тому, как мы сделали это в решении B.

Прокси-сервер не обязательно должен работать в том же домене, что и ваше приложение, если сам прокси должным образом поддерживает CORS при взаимодействии с клиентом. Связь между прокси и API не обязательно должна поддерживать CORS.

Вы можете написать свою платформу или использовать готовое решение, например https://www.npmjs.com/package/cors-anywhere.

Помните, что такой подход может представлять угрозу безопасности, если вы хотите поддерживать учетные данные.

Подробнее о CORS

Если вы хотите узнать больше о CORS, я рекомендую ознакомиться с подробной статьей MDN.