Как избежать предварительных запросов CORS в одностраничных приложениях? - PullRequest
4 голосов
/ 25 апреля 2020

Я хочу создать одностраничное приложение (SPA) и заметил следующую проблему при разделении базовых API (REST) ​​и ресурсов внешнего интерфейса (stati c vue. js code) :

При обслуживании индекса. html из другого домена, отличного от бэкэнда API, большинство запросов POST / PUT вызывают предварительный запрос CORS.

Я провел некоторое исследование и обнаружил, что в блогах [1] [2] обсуждается этот вопрос - без практического решения. Некоторые заголовки (например, заголовок Authorization и заголовок Content-Type со значением application / json) не допускаются в качестве cors-safelisted-request-header. Таким образом, запросы POST / PUT вызывают предварительный запрос CORS. Это неприемлемо, поскольку добавляет значительную задержку.

Вопрос

Можно ли избежать этих предварительных запросов, если оба домена принадлежат одному и тому же объекту?

Исследование

Я провел некоторое исследование о том, как избежать запросов CORS между внешним и внутренним интерфейсом. Решение требует, чтобы файл index. html обслуживался из того же домена, что и серверная часть REST API (см. Пример ниже). Интересно, не является ли использование отдельных доменов единственным решением, позволяющим избежать запросов CORS на SPA.

Сценарий (пример)

  • Одностраничное приложение (SPA); внешний и внутренний уровни
  • , размещенные в облаке AWS
  • Уровень 1: CDN CloudFront с источником сегмента S3 - обслуживает ресурсы c (Vue. js внешний интерфейс) на stati c .example.com
  • Уровень 2: Балансировщик нагрузки с интеграцией ECS, который запускает контейнеры node.js для размещения серверной части (REST) ​​на примере . com
  • Связь между уровнем 1 и уровнем 2 использует протокол HTTPS и парадигму REST.
  • Индекс. html обслуживается уровнем 2, и клиенты открывают веб-приложение, используя example.com .
  • Индекс. html ссылается на API, указывая на тот же домен ( example.com ). Он ссылается на активы stati c vue, указывая на CDN ( stati c .example.com ).
  • SPA состоит из двух частей: c активов (. js файлов,. css файлов и c.) И b) индексный файл. html. Последний обслуживается тем же парком внутренних контейнеров, в которых также размещается API REST.

Ссылки

[1] https://www.freecodecamp.org/news/the-terrible-performance-cost-of-cors-api-on-the-single-page-application-spa-6fcf71e50147/
[2] https://developer.akamai.com/blog/2015/08/17/solving-options-performance-issue-spas

Ответы [ 4 ]

4 голосов
/ 06 мая 2020

Как избежать предварительных запросов CORS в одностраничных приложениях?

Во-первых, вы не можете "избежать" чего-то, что является частью стандарта. Во-вторых, этот вопрос сформулирован неправильно, поскольку сами SPA могут использовать или не использовать CORS. Это полностью зависит от вашей установки / дизайна. Если вы хотите избежать предварительной проверки, просто не запрашивайте ресурсы из других источников.

Можно ли избежать этих предварительных запросов, если оба домена принадлежат одному и тому же объекту?

Нет.

Владение сайтом не имеет ничего общего с CORS. Обмен ресурсами между источниками означает, что вы хотите обмениваться ресурсами между различными источниками . Не обязательно между разными доменами. Происхождение не является ни сущностью, ни владельцем. Origin - это только часть URL-адреса.

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

Добавление нескольких источников и доменов в настройку добавляет больше проблем, чем решает. В идеале вся ваша настройка должна быть скрыта от подключающихся клиентов и сведена к одному домену и источнику.

Требуемая настройка

                     -> static file server
CDN -> Load balancer -> api server
                     -> api server
                     -> ...

Пример конфигурации

Использование балансировщика нагрузки и его правила ACL. Скажите, чтобы все трафики c были направлены туда, куда go. Я буду использовать haproxy в качестве примера, потому что это то, что я использую, и потому что, насколько я знаю, это в значительной степени отраслевой стандарт с точки зрения программного решения для балансировки нагрузки.

Это не так весь конфиг, только соответствующая часть, касающаяся маршрутизации трафика c.

# part of haproxy configuration file, usually located at /etc/haproxy/haproxy.cfg

frontend http-in # this is where requests get in to load balancer
  bind *:80
  acl data path_beg /api  # "catch" any request with path beginning with "/api"
  use_backend api if data # then route it to api backend defined below
  default_backend static  # any non-matching request we direct to static file server

backend static
  server node1 127.0.0.1:3000 # server hosting static files (index.html)

backend api
  server node1 127.0.0.1:4000 # application servers
  server node2 ...

4 голосов
/ 30 апреля 2020

Вариантов не так много.

Самое простое решение - доставить html и ресурсы из того же домена, что и ваш API.

Второй вариант - использовать только заголовки cors-safelisted-request-header.

Что я заметил:

Заголовок Content-Type можно заменить заголовком Accept. С этим заголовком все в порядке.

Если вы выполняете запросы XHR, вы можете опустить заголовок Authentication и вместо этого автоматически добавить информацию об аутентификации, установив для поля withCredentials вашего запроса XHR значение true. Vanilla JS пример:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.example.org/api/whatever', true);
xhr.withCredentials = true;
xhr.send();

Если вы используете любой другой клиент XHR, обратитесь к документации, если этот параметр можно установить.

Другой вариант - аутентификация с помощью файлов cookie и сервера. побочные занятия. Так как вы используете AWS, AWS Cognito может быть вариантом.

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

2 голосов
/ 02 мая 2020

Access-Control-Max-Age может помочь:

Заголовок Access-Control-Max-Age ответа указывает, как долго результаты предварительного запроса (то есть информация, содержащаяся в Access-Control-Allow-Methods и Access-Control-Allow-Headers заголовки) могут быть кэшированы.

... На самом деле ссылка , которую вы разместили, упоминает это:

Вы можете сказать, да. Мы можем использовать заголовок Access-Control-Max-Age для кэширования результатов предварительного запроса.

Затем они go с предупреждением:

Способ Кеш предпечатной проверки работает по URL, а не только по источнику. Это означает, что любое изменение в пути (которое включает параметры запроса) требует другого предварительного запроса.

Но вы можете продолжать использовать тот же URL-адрес и отправлять все параметры в теле запроса.

Также

Можно ли избежать этих предварительных запросов, если оба домена принадлежат одному и тому же объекту?

Нет. У браузера нет практического способа проверить право собственности на домены. Иногда даже для человека это непростая задача.

0 голосов
/ 06 мая 2020

Из того, что я понимаю, вы хотите избежать ограничений CORS. Чтобы сделать это, вы должны изменить файл web.config на вашем веб-сайте, чтобы разрешить использование CORS следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
   <httpProtocol>
     <customHeaders>
       <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
   </httpProtocol>
 </system.webServer>
</configuration> 

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

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