Что нужно сделать на клиентской части для запроса с одного сайта к другому?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Запросы между доменами (CORS): клиентская часть
Когда фронтенд-приложение на одном домене делает запрос к API на другом домене, это называется cross-origin request (кросс-доменный запрос). Браузер защищает такие запросы с помощью CORS (Cross-Origin Resource Sharing) политики. На клиентской стороне требуется понимание этого механизма и правильная конфигурация.
Основное понятие: Same-Origin Policy
Браузер по умолчанию разрешает запросы только с одного источника (Origin). Origin состоит из схемы, домена и порта:
https://example.com:443 (Origin)
|
├- Схема: https://
├- Домен: example.com
└- Порт: 443
Same Origin (разрешено):
- https://example.com -> https://example.com/api (ОК)
- https://example.com -> https://example.com:443 (ОК, порт совпадает)
Cross Origin (запрещено без CORS):
- https://example.com -> https://api.example.com (Разные домены)
- https://example.com:443 -> https://example.com:3000 (Разные порты)
- https://example.com -> http://example.com (Разные схемы)
CORS Prelight запрос
Для "сложных" запросов браузер автоматически отправляет prefligh запрос OPTIONS перед основным запросом:
fetch('https://api.other-domain.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Custom-Header': 'value'
},
body: JSON.stringify({ name: 'Alice' })
});
// Браузер сначала отправляет OPTIONS запрос:
// OPTIONS /data HTTP/1.1
// Origin: https://example.com
// Access-Control-Request-Method: POST
// Access-Control-Request-Headers: Content-Type, Custom-Header
//
// Если сервер разрешит, тогда отправляется основной POST запрос
Простые vs Сложные запросы
Простые запросы (Simple Requests) - не требуют prefligh:
// GET запросы
fetch('https://api.other-domain.com/data')
// POST с текстом
fetch('https://api.other-domain.com/data', {
method: 'POST',
headers: {
'Content-Type': 'text/plain'
},
body: 'simple text'
})
// HEAD запросы
fetch('https://api.other-domain.com/data', {
method: 'HEAD'
})
Сложные запросы (Complex Requests) - требуют prefligh:
// POST с JSON
fetch('https://api.other-domain.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Alice' })
})
// PUT, DELETE, PATCH
fetch('https://api.other-domain.com/data/123', {
method: 'DELETE'
})
// С пользовательскими заголовками
fetch('https://api.other-domain.com/data', {
headers: {
'X-Custom-Header': 'value'
}
})
// С credentials
fetch('https://api.other-domain.com/data', {
credentials: 'include'
})
Что клиент может сделать
1. Отправить запрос с указанием credentials
// Отправлять cookies с запросом
fetch('https://api.other-domain.com/data', {
method: 'GET',
credentials: 'include', // Отправляет cookies
headers: {
'Authorization': 'Bearer token123'
}
});
// Варианты credentials:
// 'omit' - не отправлять cookies (по умолчанию)
// 'same-origin' - отправлять только для same-origin
// 'include' - отправлять всегда (требует правильные CORS заголовки)
2. Установить правильные заголовки
fetch('https://api.other-domain.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
'Accept': 'application/json'
},
body: JSON.stringify({
name: 'Alice',
email: 'alice@example.com'
})
})
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.catch(error => {
console.error('CORS error:', error);
});
3. Использовать axios с конфигурацией CORS
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: 'https://api.other-domain.com',
headers: {
'Content-Type': 'application/json'
},
withCredentials: true // Эквивалент credentials: 'include'
});
axiosInstance.get('/data')
.then(response => console.log(response.data))
.catch(error => console.error('CORS error:', error));
4. Обработать CORS ошибку
fetch('https://api.other-domain.com/data')
.then(response => {
// CORS ошибка может быть перехвачена
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
// CORS ошибка будет здесь
if (error instanceof TypeError) {
console.error('CORS Error:', error.message);
console.error('Check server CORS configuration');
}
});
5. Использовать JSONP как альтернатива (устаревший способ)
// JSONP - обходит CORS, но устаревший подход
const script = document.createElement('script');
script.src = 'https://api.other-domain.com/data?callback=handleData';
document.head.appendChild(script);
window.handleData = function(data) {
console.log(data);
};
6. Использовать proxy на своем сервере
// Клиент запрашивает свой сервер
fetch('/api/proxy/data', {
method: 'GET'
})
.then(res => res.json())
.then(data => console.log(data));
// Сервер (Node.js/Express)
app.get('/api/proxy/data', async (req, res) => {
const response = await fetch('https://api.other-domain.com/data');
const data = await response.json();
res.json(data);
});
Практические примеры
1. GET запрос с API ключом
const API_KEY = 'your-api-key';
const API_URL = 'https://api.external-service.com';
fetch(`${API_URL}/users`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Accept': 'application/json'
}
})
.then(res => res.json())
.then(data => console.log('Users:', data))
.catch(error => console.error('Error:', error));
2. POST запрос с JSON
fetch('https://api.external-service.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
credentials: 'include',
body: JSON.stringify({
email: 'user@example.com',
name: 'John Doe'
})
})
.then(res => {
if (res.status === 401) {
throw new Error('Unauthorized');
}
return res.json();
})
.then(data => console.log('Success:', data))
.catch(error => {
if (error instanceof TypeError) {
console.error('CORS Error: ', error);
} else {
console.error('Error:', error.message);
}
});
3. Обработка различных HTTP методов
const baseURL = 'https://api.other-domain.com';
// DELETE запрос
fetch(`${baseURL}/users/123`, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer token'
}
});
// PUT запрос
fetch(`${baseURL}/users/123`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ name: 'Updated Name' })
});
// PATCH запрос
fetch(`${baseURL}/users/123`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ name: 'Updated Name' })
});
4. React пример с обработкой CORS
import { useState, useEffect } from 'react';
function ExternalDataFetch() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch('https://api.other-domain.com/data', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer token123'
},
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to fetch data');
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
console.error('CORS Error:', err);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{JSON.stringify(data)}</div>;
}
Таблица: Требуемые заголовки
| Заголовок | Назначение | Пример |
|---|---|---|
| Origin | Origin запроса (автоматический) | Origin: https://example.com |
| Authorization | Аутентификация | Authorization: Bearer token123 |
| Content-Type | Тип контента | Content-Type: application/json |
| Accept | Желаемый формат ответа | Accept: application/json |
| Custom-Header | Свой заголовок | X-API-Version: v1 |
Проверка CORS в браузере
// DevTools -> Network tab -> найти запрос
// Response Headers должны содержать:
// Access-Control-Allow-Origin: https://example.com
// Access-Control-Allow-Methods: GET, POST, PUT
// Access-Control-Allow-Headers: Content-Type, Authorization
// Access-Control-Allow-Credentials: true
// Если этих заголовков нет - сервер не настроен для CORS
Типичные CORS ошибки
Access to XMLHttpRequest at 'https://api.other.com/data' from origin
'https://myapp.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
-> Решение: Сервер должен отправлять
Access-Control-Allow-Origin: https://myapp.com
Access to XMLHttpRequest ... has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
Expected 'true' in CORS header 'Access-Control-Allow-Credentials'.
-> Решение: Добавить withCredentials: true на клиенте
и Access-Control-Allow-Credentials: true на сервере
Вывод
На клиентской стороне нужно:
- Установить правильные заголовки (Content-Type, Authorization)
- Использовать
credentials: 'include'если нужны cookies - Обработать CORS ошибки
- Использовать fetch API или axios
Но самое важное:
- CORS настраивается на сервере, не на клиенте
- Клиент может только правильно отправить запрос
- Сервер должен отправить правильные CORS заголовки в ответе
- Если CORS не работает - проблема в конфигурации сервера
Альтернативы:
- Proxy на своем сервере
- JSONP (устаревший способ)
- WebSockets (для реал-тайма)
- Same-domain архитектура