>>
Проблема: сервер ожидает данные в формате HTML-формы
Когда вы отправляете HTML-форму с method="POST" и стандартным enctype="application/x-www-form-urlencoded", браузер кодирует данные как строку вида key1=value1&key2=value2. Сервер, который обрабатывает такие запросы, не понимает JSON — он ждёт именно этот формат.
Решение — отправить данные в формате URL-кодированной строки и указать правильный Content-Type.
Способ 1: Ручное формирование строки (самый простой)
const data = {
name: 'John',
age: 30
};
// Ручное кодирование в URL-формат
const formBody = Object.keys(data)
.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
.join('&');
fetch('/api/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formBody
})
.then(response => response.json())
.then(result => console.log('Успех:', result))
.catch(error => console.error('Ошибка:', error));
Функция encodeURIComponent() гарантирует, что специальные символы (пробелы, &, =, кириллица) будут корректно закодированы.
Способ 2: Использование FormData (более гибкий)
Объект FormData автоматически кодирует данные в нужный формат. Он удобен, если у вас есть форма в DOM, или вы хотите добавить файлы:
const formData = new FormData();
formData.append('name', 'John');
formData.append('age', 30);
fetch('/api/submit', {
method: 'POST',
// Content-Type НЕ указываем — fetch сам установит multipart/form-data
// для FormData с файлами, или application/x-www-form-urlencoded для простых данных
body: formData
})
.then(response => response.json())
.then(result => console.log('Успех:', result));
Важное замечание: Если вы используете FormData только с текстовыми полями (без файлов), fetch автоматически установит Content-Type: multipart/form-data. Некоторые серверы могут это не принять. В таком случае лучше использовать первый способ.
Способ 3: URLSearchParams (современный и чистый)
API URLSearchParams специально создан для работы с URL-кодированными данными:
const params = new URLSearchParams();
params.append('name', 'John');
params.append('age', 30);
fetch('/api/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString() // преобразует в "name=John&age=30"
})
.then(response => response.json())
.then(result => console.log('Успех:', result));
Этот способ предпочтительнее, потому что URLSearchParams сам заботится о правильном кодировании всех значений.
Что выбрать?
- Для простых данных без файлов — используйте
URLSearchParams (способ 3).
- Для форм с файлами — используйте
FormData (способ 2).
- Если нужен полный контроль — ручное кодирование (способ 1).
Все три способа отправят данные в формате, который сервер воспримет как отправку обычной HTML-формы.
<<>>
Альтернатива: создать скрытую форму и отправить её программно
Если сервер очень придирчивый или вам нужно имитировать поведение формы максимально точно (например, для редиректа на страницу результата), можно создать скрытую HTML-форму в DOM и отправить её через JavaScript:
function submitAsForm(url, data) {
const form = document.createElement('form');
form.method = 'POST';
form.action = url;
form.style.display = 'none';
for (const [key, value] of Object.entries(data)) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = value;
form.appendChild(input);
}
document.body.appendChild(form);
form.submit(); // браузер отправит форму как обычный POST-запрос
document.body.removeChild(form); // очистка
}
// Использование:
submitAsForm('/api/submit', { name: 'John', age: 30 });
Минусы этого подхода:
- Вы теряете контроль над ответом — браузер выполнит переход на URL, указанный в
action.
- Нельзя обработать ответ в JavaScript (нет
fetch/XMLHttpRequest).
Этот метод стоит использовать только если вам действительно нужно, чтобы страница перезагрузилась (как при реальной отправке формы). Для AJAX-запросов лучше применять способы из первого ответа.