Как устранить проблемы с cors
Перейти к содержимому

Как устранить проблемы с cors

  • автор:

Ошибки CORS, проблемы кросс-сайтовых AJAX-запросов и их решение

Статья также доступна на украинском (перейти к просмотру).

Ошибки CORS, проблемы кросс-сайтовых AJAX-запросов и их решение

Оглавление

  • Условия
  • Решение
  • Что такое Cors перекрестный запрос?
    • Простые запросы
    • Предварительные запросы
    • Запросы с учетными данными

    XMLHttpRequest — это API, используемое JS-скриптами для отправки запросов на сервер. Часто используется для создания интерактивных страниц с загрузкой данных на лету без перезагрузки страницы. Использование такого API очень популярно, но в целях безопасности по умолчанию можно отправлять запросы только в рамках одного домена. Такая безопасность организована благодаря использованию CORS, который ограничивает все кросс-сайтовые HTTP-запросы.

    С появлением спецификации Cross Origin Resource Sharing (CORS), которая теперь является кандидатом в рекомендации W3C, разработчики веб-приложений получили поддерживаемый браузером механизм для безопасного выполнения XmlHttpRequests в другом домене.

    На момент написания этой статьи мы наконец можем сказать, что CORS поддерживается всеми основными браузерами. Первоначально он появился в Firefox 3.5, Safari 4 и Chrome 3. Internet Explorer 10 теперь имеет встроенную поддержку.

    В окне браузера появляется одно из следующих сообщений:

    • Ошибка загрузки заголовка No Access-Control-Allow-Origin.
    • Запрос между источниками заблокирован: политика «Тот же источник» запрещает чтение удаленного ресурса. Причина: заголовок CORS «Access-Control-Allow-Origin» отсутствует.
    • Не удалось загрузить ответ на предварительный запрос не прошел проверку управления доступом: в запрошенном ресурсе отсутствует заголовок Access-Control-Allow-Origin.

    Одним из ключевых слов, которое обычно упоминается в сообщениях, является Access-Control-Allow-Origin.

    Условия

    Веб-приложение на основе браузера пытается выполнить вызов «независимо от источника», чтобы получить «общий ресурс» от внешней веб-службы. Это называется запросом CORS (Cross-Origin-Resource-Sharing).

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

    Решение

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

    Когда один источник (www.ursprung1.com) запрашивает другой источник (www.ursprung2.com), это называется запросом между источниками. Чтобы этот запрос работал, должны быть выполнены определенные условия. Вызванная внешняя служба (www.origin2.com) должна вернуть в своем ответе HTTP-заголовок Access-Control-Allow-Origin.

    Если внешняя служба не вернет этот заголовок, соответствие браузера спецификации CORS остановит запрос и вернет одну из указанных выше ошибок.

    Что такое Cors перекрестный запрос?

    Если скрипт на вашей странице запускается из домена mydomain.com и хотел бы запросить ресурс через XmlHttpRequest или XDomainRequest из домена otherdomain.com, это запрос из другого источника. Исторически сложилось так, что из соображений безопасности эти типы запросов были запрещены браузерами.

    Простые запросы

    Для простых межсайтовых запросов (то есть GET и POST, которые не устанавливают настраиваемые заголовки, а тело запроса представляет собой обычный текст или данные формы) браузер просто включает дополнительные заголовки Origin и Referrer, указывающие запрашивающий домен.

    Обратите внимание, что браузер будет включать заголовок Origin только в том случае, если запрос является перекрестным. Ведь основная причина — отсутствует заголовок — cors access control allow origin.

    Когда браузер увидит, что значение Access-Control-Allow-Origin соответствует домену страницы, он разрешит обработку ответа. Сервер может установить значение «*» в этом заголовке, чтобы указать, что это общедоступный ресурс, который допускает любое происхождение.

    Предварительные запросы

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

    Запросы с учетными данными

    Браузер не будет отправлять файлы cookie или данные проверки подлинности HTTP в междоменном запросе XmlHttpRequest. Клиентское приложение должно указать, что они должны быть отправлены, задав свойство withCredentials запроса XmlHttpRequest или XDomainRequest. По умолчанию это значение равно false и не установлено.

    Обратите внимание, что для этого необходимо использовать jQuery 1.5.1+, поскольку предыдущие версии jQuery не передавали свойство withCredentials в собственный XmlHttpRequest.

    Обязательное примечание к Internet Explorer

    Internet Explorer 8 и 9 имеют ограниченную поддержку CORS. А именно:

    • Поддерживаются только GET и POST с типом содержимого обычный/текст.
    • Он не поддерживает предварительную проверку
    • К запросу нельзя добавлять настраиваемые заголовки.
    • Запросы учетных данных не поддерживаются
    • Запросы должны быть направлены на ту же схему, что и страница хостинга.

    Internet Explorer 10 теперь имеет встроенную поддержку CORS. Однако на момент написания этой статьи IE 10 поддерживает только запросы с учетными данными между доменами, которые имеют совпадающее доменное имя второго уровня, например, a.mydomain.com запрашивает b.mydomain.com. Пока невозможно отправлять учетные данные между mydomain.com и otherdomain.com.

    Тестирование

    Отличный ресурс для тестирования запросов CORS можно найти по адресу test.cors.org. Этот тестовый сайт позволяет:

    • Отправлять запросы CORS на удаленный сервер для проверки его возможностей.
    • Отправка запросов CORS на тестовый сервер для изучения функций CORS.

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

    Каков начальный URL вызова (т. е. источник)? Иногда это можно найти в самом сообщении об ошибке.

    Какой URL извлекается из внешней службы? Иногда это можно найти в сообщении об ошибке консоли.

    Информация об ошибке в консоли браузера

    Что извлекается и почему? Это PNG-файл? Скрипт, CSS или файл шрифта? Что именно извлекается и для чего используется? Это может пролить свет на вариант использования и показать, почему актив так важен в этом удаленном месте.

    Требует ли этот внешний ресурс аутентификацию? Если требуется переадресация, заголовок ответа Access-Control-Allow-Origin может не возвращаться, и вызов завершится ошибкой. Скопируйте URL-адрес ресурса прямо в новую вкладку инкогнито в браузере. Это может быть хорошей проверкой того, можно ли получить к нему доступ при общих обстоятельствах, но это не гарантирует, что он будет работать в коде веб-приложения.

    Информация об ошибке CORS в браузере.

    Видите ли вы вызов метода HTTP OPTIONS на вкладке «Сеть» браузера? Если настраиваемые заголовки запроса, проверка подлинности или другие условия присутствуют в запросе между источниками, браузер выполняет дополнительный HTTP-вызов. Это также известно как «предполетный вызов». Это явно не указано в коде веб-приложения. Браузер в фоновом режиме создает его и делает частью спецификации CORS.

    Когда выполняется этот вызов OPTIONS, в ответ от этого вызова должны быть включены определенные значения, чтобы он был успешным и был выполнен фактический HTTP-вызов к ресурсу. Если вызов OPTIONS завершается неудачно, ресурс не извлекается и в консоли браузера появляется ошибка CORS.

    Запишите, когда вы видите вызов OPTIONS. Также обратите внимание, происходит ли вызов с переадресацией (состояние 302) непосредственно перед вызовом OPTIONS.

    Если при вызове OPTIONS происходит переадресация, вызов OPTIONS, скорее всего, завершится ошибкой. Это означает, что вызов ресурса также завершится ошибкой CORS.

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

    Еще один пример ошибки CORS

    Генерация HAR-файла. Если вы можете получить моментальный снимок неудачного вызова и событий до и после него, вы сможете лучше устранить проблему и избежать необходимости ее воспроизведения пользователем. Заголовки в запросах и ответах могут быть проверены вместе с OPTIONS вызовами и переадресациями!

    Подписывайтесь на наш телеграм-канал https://t.me/freehostua, чтобы быть в курсе новых полезных материалов.

    Мы в чем-то ошиблись, или что-то пропустили?

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

    Причина: отсутствует заголовок CORS «Access-Control-Allow-Origin»

    В ответе на CORS-запрос отсутствует заголовок Access-Control-Allow-Origin , используемый для проверки, может ли ресурс быть доступен для контента на текущем домене.

    Если у вас есть доступ к серверу, то добавьте домен запрашивающего сайта в список разрешённых доменов, добавив его в значение заголовка Access-Control-Allow-Origin .

    Например, для предоставления сайту https://amazing.site доступа к ресурсам с использованием CORS, заголовок должен выглядеть так:

    Access-Control-Allow-Origin: https://amazing.site

    Также вы можете разрешить доступ любому сайту, используя подстановку * . Используйте этот способ только для публичных API. В закрытых API * не должна использоваться, вместо этого должен быть установлен определённый домен или домены. При этом подстановка работает только для запросов с атрибутом crossorigin со значением anonymous .

    Access-Control-Allow-Origin: *

    Предупреждение: Внимание: Использование * для доступа к закрытым API — плохая идея по очевидным причинам.

    Чтобы разрешить любому сайту делать CORS-запросы без использования подстановки * (например, для включения авторизационных данных), ваш сервер должен считывать значение заголовка Origin из запроса и использовать это значение, чтобы задать Access-Control-Allow-Origin , а также выставить заголовок Vary: Origin , чтобы обозначить динамическую установку заголовка в зависимости от источника.

    Конкретная директива для установки заголовков зависит от вашего сервера. Так в Apache нужно добавить следующую строку в конфигурацию сервера (в соответствующих разделах , , или ). Конфигурация обычно находится в файле с расширением .conf (стандартные названия: httpd.conf , apache.conf ), либо в файле .htaccess .

    Header set Access-Control-Allow-Origin 'origin-list'

    В Nginx для установки этого заголовка используется команда:

    add_header 'Access-Control-Allow-Origin' 'origin-list'

    Как решить проблему с блокировкой CORS?

    Изучаю react/node по курсу udemy. Локально все работает нормально, но после деплоя при попытке Login with Google, консоль выдает ошибку:

    Failed to load resource: the server responded with a status of 503 (Service Unavailable)

    Access to fetch at ‘https://geostickers.herokuapp.com/graphql’ from origin ‘https://geoapp-pvfinzglt.now.sh’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

    Error logging in TypeError: Failed to fetch

    Ошибку можно сгенерировать на клиенте https://geoapp-pvfinzglt.now.sh

    const server = new ApolloServer(< typeDefs, resolvers, //cors: true, - не решает проблему context: async (< req >) => < let authToken = null; let currentUser = null; try < authToken = req.headers.authorization if(authToken) < currentUser = await findOrCreateUser(authToken); >> catch(err) < console.error(`Unable to authenticate user with token $`) > return < currentUser >> >); server.listen(< port: process.env.PORT || 4000 >).then((< url >) => < console.log(`Server listening on $`) >);
    • Вопрос задан более трёх лет назад
    • 29455 просмотров

    1 комментарий

    Средний 1 комментарий

    Несколько советов по работе с CORS для начинающих разработчиков

    В этой статье мы с вами разберемся, что такое CORS, CORS-ошибки и из-за чего мы можем с ними сталкиваться. Я также продемонстрирую возможные решения и объясню, что такое предварительные (preflight) запросы, CORS-заголовки и в чем заключается их важность при обмене данными между сторонами. Эта статья рассчитана на тех, у кого уже есть базовые познания в области веб-разработки и некоторый опыт с протоколом HTTP. Я старался писать статью так, чтобы она была понятна и новичкам, будучи наполненной знаниями, но при этом стараясь избегать слишком большого количества технических нюансов, не связанных с темой CORS. Если вы заметите какие-либо ошибки или у вас будут предложения, не стесняйтесь писать мне. В некоторых местах я нарочно делал упрощения, говоря “служба”, подразумевая “сервер”, и наоборот.

    Что такое CORS?

    Cross-Origin Resource Sharing (CORS или “совместное использование ресурсов различными источниками”) — это контролируемый и применяемый в принудительном порядке клиентом (браузером) механизм обеспечения безопасности на основе HTTP. Он позволяет службе (API) указывать любой источник (origin), помимо себя, из которого клиент может запрашивать ресурсы. Он был разработан в соответствии с same-origin policy (SOP или “политика одинакового источника”), которая ограничивает взаимодействие сайта (HTML-документа или JS-скрипта), загруженного из одного источника, с ресурсом из другого источника. CORS используется для явного разрешения определенных cross-origin запросов и отклонения всех остальных.

    В основном CORS реализуют веб-браузеры, но как вариант его также можно использовать в API-клиентах. Он присутствует во всех популярных браузерах, таких как Google Chrome, Firefox, Opera и Safari. Этот стандарт был принят в качестве рекомендации W3C в январе 2014 года. Исходя из этого, можно смело предполагать, что он реализован во всех доступных в настоящее время браузерах, которые не были перечислены выше.

    Как это работает?

    Все начинается на стороне клиента, еще до отправки основного запроса. Клиент отправляет в службу с ресурсами предварительный (preflight) CORS-запрос с определенными параметрами в заголовках HTTP (CORS-заголовках, если быть точнее). Служба отвечает, используя те же заголовки с другими или такими же значениями. На основе ответа на предварительный CORS-запрос клиент решает, может ли он отправить основной запрос к службе. Браузер (клиент) выдаст ошибку, если ответ не соответствует требованиям предварительной проверки CORS.

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

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

    Что такое предварительная проверка CORS?

    Когда браузер отправляет запрос на сервер, он сначала отправляет Options HTTP-запрос. Это и есть предварительным CORS-запрос. Затем сервер отвечает списком разрешенных методов и заголовков. Если браузеру разрешено сделать фактический запрос, то тогда он незамедлительно отправит его. Если нет, он покажет пользователю ошибку и не выполнит основной запрос.

    CORS-заголовки

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

    • Access-Control-Allow-Origin указывает, какой источник может получать ресурсы. Вы можете указать один или несколько источников через запятую, например: https://foo.io,http://bar.io .
    • Access-Control-Allow-Methods указывает, какие HTTP-методы разрешены. Вы можете указать один или несколько HTTP-методов через запятую, например: GET,PUT,POST .
    • Access-Control-Allow-Headers указывает, какие заголовки запросов разрешены. Вы можете указать один или несколько заголовков через запятую, например: Authorization,X-My-Token .
    • Access-Control-Allow-Credentials указывает, разрешена ли отправка файлов cookie. По умолчанию: false .
    • Access-Control-Max-Age указывает в секундах, как долго должен кэшироваться результат запроса. По умолчанию: 0.

    Если вы решите использовать Access-Control-Allow-Credentials=true , то вам нужно знать, что вы не сможете использовать символы * в заголовках Access-Control-Allow-* . Необходимо будет явно перечислить все разрешенные источники, методы и заголовки.

    Полный список CORS-заголовков вы можете найти здесь.

    Почему запрос может быть заблокирован политикой CORS?

    Если вы веб-разработчик, вы, вероятно, уже слышали или даже сталкивались с CORS-ошибками, имея за плечами часы, потраченные на поиски их причин и решений. Наиболее распространенная проблема заключается в том, что браузер блокирует запрос из-за политики CORS. Браузер выдаст ошибку и отобразит в консоли следующее сообщение:

    Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    Приведенная выше CORS-ошибка уведомляет пользователя о том, что браузер не может получить доступ к ресурсу ( https://localhost:8080 ) из источника ( https://localhost:3000 ), поскольку сервер его не одобрил. Это произошло из-за того, что сервер не ответил заголовком Access-Control-Allow-Origin с этим источником или символом * в ответе на предварительный CORS-запрос.

    Запрос может быть заблокирован политикой CORS не только из-за невалидного источника, но и из-за неразрешенных заголовка HTTP, HTTP-метода или заголовка Cookie.

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

    Фундаментальная идея “исправления CORS” заключается в том, чтобы отвечать на OPTIONS запросы, отправленные от клиента, корректными заголовками. Есть много способов начать отвечать корректными CORS. Вы можете использовать прокси-сервер или какое-нибудь middleware на своем сервере.

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

    1. Настройка вашего сервера

    По умолчанию, если вы являетесь владельцем сервера, вам необходимо настроить на своем сервере CORS-ответы, и это единственный способ правильно решить проблему. Вы можете добиться этого несколькими способами из нескольких слоев вашего приложения. Самый распространенный способ — использовать обратный прокси-сервер (reverse-proxy), API-шлюз или любой другой сервис маршрутизации, который позволяет добавлять заголовки к ответам. Для этого можно использовать множество сервисов, и вот некоторые из них: HAProxy, Linkerd, Istio, Kong, nginx, Apache, Traefik. Если ваша инфраструктура содержит только приложение без каких-либо дополнительных слоев, то вы можете добавить поддержку CORS в код самого приложения.

    Вот несколько популярных примеров активации CORS:

    • Apache: отредактируйте файл .htaccess
    • Nginx: отредактируйте файл конфигурации,
    • Traefik: используйте middleware,
    • Spring Boot: используйте аннотацию @EnableCORS,
    • ExpressJS: используйте app.use(cors()),
    • NextJS: используйте реквест-хелперы.

    Здесь вы можете найти больше примеров активации CORS для разных фреймворков и языков: enable-cors.org.

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

    2. Установка расширения для браузера

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

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

    Вы можете найти расширения в Google Web Store или в библиотеке дополнений Mozilla. В некоторых случаях дефолтной конфигурации расширения может быть недостаточно; убедитесь, что установленное расширение корректно настроено. Вам также следует быть в курсе, что если держать подобное расширение включенным постоянно, то это может вызвать проблемы с некоторыми сайтами. Их рекомендуется использовать только в целях разработки.

    3. Отключение CORS-проверок в браузере

    Вы можете полностью отключить CORS-проверки в своем браузере. Чтобы отключить CORS-проверки в Google Chrome, нужно закрыть браузер и запустить его с флагами —disable-web-security и —user-data-dir . После запуска Google Chrome не будет отправлять предварительные CORS-запросы и не будет проверять CORS-заголовки.

    # Windows chrome.exe --user-data-dir="C://chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials # macOS open /Applications/Google\ Chrome.app --args --user-data-dir="/var/tmp/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials # Linux google-chrome --user-data-dir="~/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials

    Все команды, приведенные выше, запускают Google Chrome в изолированной безопасной среде. Они не затронут ваш основной профиль Chrome.

    4. Настройка прокси-сервера

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

    Прокси-сервер — хорошее решение, если у вас нет доступа к службе, которую вы собираетесь использовать. Существует множество готовых к использованию решений с открытым исходным кодом для создания прокси-серверов, но вам обязательно нужно следить за тем, чтобы они не пытались перехватить ваши запросы с заголовками авторизации и передать их какой-либо сторонней службе. Такие нарушения безопасности могут привести к катастрофическим последствиям как для вас, так и для потенциальных пользователей службы.

    Ниже приведен список CORS сервисов с открытым исходным кодом, которые вы можете найти на просторах интернета:

    • https://github.com/Freeboard/thingproxy
    • https://github.com/bulletmark/corsproxy
    • https://github.com/Rob—W/cors-anywhere

    Перед использованием любого из этих сервисов обязательно проверьте код самой последний версии версии.

    Как протестировать CORS?

    Использование браузера для проверки конфигурации CORS может оказаться на удивление утомительной задачей. В качестве альтернативы вы можете использовать такие инструменты, как CORS Tester, test-cors.org или, если вас не страшит командная строка, вы можете использовать curl для проверки конфигурации CORS.

    curl -v -X OPTIONS https://simplelocalize.io/api/v1/translations

    Остерегайтесь ложных CORS-ошибок

    В некоторых случаях, когда сервис находится за дополнительным слоем защиты ограничителя частоты запросов, балансировщика нагрузки или сервера авторизации, вы можете получить ложную CORS-ошибку. Заблокированные или отклоненные сервером запросы должны отвечать статус кодами ошибки. Например:

    • 401 unauthorized,
    • 403 forbidden,
    • 429 too many requests,
    • 500 internal server error,
    • любые, кроме 2XX или 3XX.

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

    Заключение

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

    Полезные ссылки

    • https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors
    • https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
    • https://enable-cors.org/
    • https://stackoverflow.com/a/42024918/1133169

    Материал подготовлен в преддверии старта онлайн-курса «JavaScript Developer. Professional». Недавно прошел открытый урок на тему «CSS-in-JS. Удобный способ управлять стилями», на котором рассмотрели Styled components, Linaria, Astroturf и другие инструменты упрощения работы со стилями. Посмотреть запись можно по ссылке.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *