- POST form data using JavaScript’s Fetch API
- Prerequisites
- Form setup
- Add a form submit event listener
- Sending the form data
- Option #1: As FormData object
- Option #2: As a URL-enconded string
- Summary
- Related links
- XMLHttpRequest POST, формы и кодировка
- Кодировка urlencoded
- GET-запрос
- POST с urlencoded
- Кодировка multipart/form-data
- POST с multipart/form-data
- FormData
- Другие кодировки
- Итого
POST form data using JavaScript’s Fetch API
Using JavaScript’s Fetch API to send form data gives you full control of data transmission.
Rather than relying on standard HTML behavior, which immediately sends the data payload upon submit and redirects to a new page, you can handle the data transmission in the background and communicate the result efficiently through minimal DOM manipulation.
Best of all, this process is made extremely easy by in-built constructor objects in JavaScript that do most of the heavy lifting.
Table of contents
Prerequisites
Form setup
To demonstrate, we need a form.
Let’s keep things simple: a form with an id of form with which we can select it in JavaScript. The form contains two input fields named username and password. Make sure to give the inputs identifiable names because these will be the keys through which data values can be identified in the payload.
Finally, there is a button of type submit that will submit the form data as a submit event.
Add a form submit event listener
First, select the form element and add an event listener to it, listening out for a submit event. And inside the function, apply the preventDefault() method to the event object to prevent the form being submitted by HTML by default.
/* Selecting the HTML form and adding a 'submit' event listener */ const form = document.getElementById('form'); form.addEventListener('submit', function(e) < e.preventDefault() >)
Sending the form data
Option #1: As FormData object
The in-built FormData object constructor will help you do all the heavy lifting.
Pass in a form element to it, and it will convert its contents into arrays of data (one for each form element) containing two items: an element’s name attribute and the value input for the element.
Because the object produced is a special FormData object with associated methods, you cannot view its payload contents directly with console.log() . Instead, access the contained data directly using console.log([…payload]) .
/* Converting the form to a new formData object */ const form = document.getElementById('form'); form.addEventListener('submit', function(e) < // Prevent default behavior: e.preventDefault(); // Create payload as new FormData object: const payload = new FormData(form); console.log([…payload]); >)
Now, you can send the form data as a POST request using the Fetch API. We use the test server httpbin.org to make the request. It responds by sending back the request’s parameters, which is fine for testing purposes.
Importantly, do not create a Content-Type property in the options object passed in to the fetch request in the second position. This will be determined automatically as multipart/form-data (the norm for HTML forms). Entering an erroneous value could lead to an error.
/* Sending the formData object as payload using Fetch */ const form = document.getElementById('form'); form.addEventListener('submit', function(e) < // Prevent default behavior: e.preventDefault(); // Create payload as new FormData object: const payload = new FormData(form); // Post the payload using Fetch: fetch('https://httpbin.org/post', < method: 'POST', body: payload, >) .then(res => res.json()) .then(data => console.log(data)) >)
Option #2: As a URL-enconded string
You can also send the form data as a URL-encoded string. This i more efficient for text-based form data.
For this, you can also lean heavily on an in-built object constructor: URLSearchParams . Pass in a FormData object, and it will convert it to a URL-encoded string that you can then send as the payload.
/* Converting the form to a URL-encoded string */ const form = document.getElementById('form'); form.addEventListener('submit', function(e) < // Prevent default behavior e.preventDefault(); // Create new FormData object: const formData = new FormData(form); // Convert formData object to URL-encoded string: const payload = new URLSearchParams(formData); >)
The fetch request to send the payload is exactly the same as above. But this time, it will be sent as content type application/x-www-form-urlencoded . You do not need to enter a Content-Type property, as this will be set automatically.
/* Sending the URL-encoded string as payload using Fetch */ const form = document.getElementById('form'); form.addEventListener('submit', function(e) < // Prevent default behavior: e.preventDefault(); // Create new FormData object: const formData = new FormData(form); // Convert formData object to URL-encoded string: const payload = new URLSearchParams(formData); // Post the payload using Fetch: fetch('https://httpbin.org/post', < method: 'POST', body: payload, >) .then(res => res.json()) .then(data => console.log(data)) >)
For this type of payload, the browser will decode what is sent for you to view it in developer tools. For this, access (in Chrome) the Network > Fetch/XHR. Now select the post item in this view and view the Payload tab. It should look something like this:
Summary
Using JavaScript’s Fetch API to submit form data gives you full flexibility over the data transmission process, rather than relying upon the default form submission behavior of HTML.
Related links
XMLHttpRequest POST, формы и кодировка
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/xmlhttprequest.
Во время обычной отправки формы браузер собирает значения её полей, делает из них строку и составляет тело GET/POST-запроса для посылки на сервер.
При отправке данных через XMLHttpRequest , это нужно делать самим, в JS-коде. Большинство проблем и вопросов здесь связано с непониманием, где и какое кодирование нужно осуществлять.
Кодировка urlencoded
Основной способ кодировки запросов – это urlencoded, то есть – стандартное кодирование URL.
Здесь есть два поля: name=Ivan и surname=Ivanov .
Браузер перечисляет такие пары «имя=значение» через символ амперсанда & и, так как метод GET, итоговый запрос выглядит как /submit?name=Ivan&surname=Ivanov .
Все символы, кроме английских букв, цифр и — _ . ! ~ * ‘ ( ) заменяются на их цифровой код в UTF-8 со знаком %.
Например, пробел заменяется на %20 , символ / на %2F , русские буквы кодируются двумя байтами в UTF-8, поэтому, к примеру, Ц заменится на %D0%A6 .
Будет отправлена так: /submit?name=%D0%92%D0%B8%D0%BA%D1%82%D0%BE%D1%80&surname=%D0%A6%D0%BE%D0%B9 .
в JavaScript есть функция encodeURIComponent для получения такой кодировки «вручную»:
alert( encodeURIComponent(' ') ); // %20 alert( encodeURIComponent('/') ); // %2F alert( encodeURIComponent('В') ); // %D0%92 alert( encodeURIComponent('Виктор') ); // %D0%92%D0%B8%D0%BA%D1%82%D0%BE%D1%80
Эта кодировка используется в основном для метода GET, то есть для передачи параметра в строке запроса. По стандарту строка запроса не может содержать произвольные Unicode-символы, поэтому они кодируются как показано выше.
GET-запрос
Формируя XMLHttpRequest, мы должны формировать запрос «руками», кодируя поля функцией encodeURIComponent .
Например, для посылки GET-запроса с параметрами name и surname , аналогично форме выше, их необходимо закодировать так:
// Передаём name и surname в параметрах запроса var xhr = new XMLHttpRequest(); var params = 'name=' + encodeURIComponent(name) + '&surname=' + encodeURIComponent(surname); xhr.open("GET", '/submit?' + params, true); xhr.onreadystatechange = . ; xhr.send();
Браузер автоматически добавит к запросу важнейшие HTTP-заголовки, такие как Content-Length и Connection .
По спецификации браузер запрещает их явную установку, как и некоторых других низкоуровневых HTTP-заголовков, которые могли бы ввести в заблуждение сервер относительно того, кто и сколько данных ему прислал, например Referer . Это сделано в целях контроля правильности запроса и для безопасности.
Запрос, отправленный кодом выше через XMLHttpRequest , никак не отличается от обычной отправки формы. Сервер не в состоянии их отличить.
Поэтому в некоторых фреймворках, чтобы сказать серверу, что это AJAX, добавляют специальный заголовок, например такой:
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
POST с urlencoded
В методе POST параметры передаются не в URL, а в теле запроса. Оно указывается в вызове send(body) .
В стандартных HTTP-формах для метода POST доступны три кодировки, задаваемые через атрибут enctype :
В зависимости от enctype браузер кодирует данные соответствующим способом перед отправкой на сервер.
В случае с XMLHttpRequest мы, вообще говоря, не обязаны использовать ни один из этих способов. Главное, чтобы сервер наш запрос понял. Но обычно проще всего выбрать какой-то из стандартных.
В частности, при POST обязателен заголовок Content-Type , содержащий кодировку. Это указание для сервера – как обрабатывать (раскодировать) пришедший запрос.
Для примера отправим запрос в кодировке application/x-www-form-urlencoded :
var xhr = new XMLHttpRequest(); var body = 'name=' + encodeURIComponent(name) + '&surname=' + encodeURIComponent(surname); xhr.open("POST", '/submit', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = . ; xhr.send(body);
Всегда используется только кодировка UTF-8, независимо от языка и кодировки страницы.
Если сервер вдруг ожидает данные в другой кодировке, к примеру windows-1251, то их нужно будет перекодировать.
Кодировка multipart/form-data
Кодировка urlencoded за счёт замены символов на %код может сильно «раздуть» общий объём пересылаемых данных. Поэтому для пересылки файлов используется другая кодировка: multipart/form-data.
В этой кодировке поля пересылаются одно за другим, через строку-разделитель.
Чтобы использовать этот способ, нужно указать его в атрибуте enctype и метод должен быть POST:
Форма при такой кодировке будет выглядеть примерно так:
. Заголовки. Content-Type: multipart/form-data; boundary=RaNdOmDeLiMiTeR --RaNdOmDeLiMiTeR Content-Disposition: form-data; name="name" Виктор --RaNdOmDeLiMiTeR Content-Disposition: form-data; name="surname" Цой --RaNdOmDeLiMiTeR--
…То есть, поля передаются одно за другим, значения не кодируются, а чтобы было чётко понятно, какое значение где – поля разделены случайно сгенерированной строкой, которую называют «boundary» (англ. граница), в примере выше это RaNdOmDeLiMiTeR :
Сервер видит заголовок Content-Type: multipart/form-data , читает из него границу и раскодирует поля формы.
Такой способ используется в первую очередь при пересылке файлов, так перекодировка мегабайтов через urlencoded существенно загрузила бы браузер. Да и объём данных после неё сильно вырос бы.
Однако, никто не мешает использовать эту кодировку всегда для POST запросов. Для GET доступна только urlencoded.
POST с multipart/form-data
Сделать POST-запрос в кодировке multipart/form-data можно и через XMLHttpRequest.
Достаточно указать в заголовке Content-Type кодировку и границу, и далее сформировать тело запроса, удовлетворяющее требованиям кодировки.
Пример кода для того же запроса, что и раньше, теперь в кодировке multipart/form-data :
var data = < name: 'Виктор', surname: 'Цой' >; var boundary = String(Math.random()).slice(2); var boundaryMiddle = '--' + boundary + '\r\n'; var boundaryLast = '--' + boundary + '--\r\n' var body = ['\r\n']; for (var key in data) < // добавление поля body.push('Content-Disposition: form-data; name="' + key + '"\r\n\r\n' + dataJavascript function post form + '\r\n'); >body = body.join(boundaryMiddle) + boundaryLast; // Тело запроса готово, отправляем var xhr = new XMLHttpRequest(); xhr.open('POST', '/submit', true); xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary); xhr.onreadystatechange = function() < if (this.readyState != 4) return; alert( this.responseText ); >xhr.send(body);
Тело запроса будет иметь вид, описанный выше, то есть поля через разделитель.
Можно создать запрос, который сервер воспримет как загрузку файла.
Для добавления файла нужно использовать тот же код, что выше, модифицировав заголовки перед полем, которое является файлом, так:
Content-Disposition: form-data; name="myfile"; filename="pic.jpg" Content-Type: image/jpeg (пустая строка) содержимое файла
FormData
Современные браузеры, исключая IE9- (впрочем, есть полифил), поддерживают встроенный объект FormData, который кодирует формы для отправки на сервер.
Это очень удобно. Например:
Этот код отправит на сервер форму с полями name , surname и patronym .
- Конструктор new FormData([form]) вызывается либо без аргументов, либо с DOM-элементом формы.
- Метод formData.append(name, value) добавляет данные к форме.
Объект formData можно сразу отсылать, интеграция FormData с XMLHttpRequest встроена в браузер. Кодировка при этом будет multipart/form-data .
Другие кодировки
XMLHttpRequest сам по себе не ограничивает кодировку и формат пересылаемых данных.
Поэтому для обмена данными часто используется формат JSON:
var xhr = new XMLHttpRequest(); var json = JSON.stringify(< name: "Виктор", surname: "Цой" >); xhr.open("POST", '/submit', true) xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); xhr.onreadystatechange = . ; // Отсылаем объект в формате JSON и с Content-Type application/json // Сервер должен уметь такой Content-Type принимать и раскодировать xhr.send(json);
Итого
- У форм есть две основные кодировки: application/x-www-form-urlencoded – по умолчанию и multipart/form-data – для POST запросов, если явно указана в enctype . Вторая кодировка обычно используется для больших данных и только для тела запроса.
- Для составления запроса в application/x-www-form-urlencoded используется функция encodeURIComponent .
- Для отправки запроса в multipart/form-data – объект FormData .
- Для обмена данными JS ↔ сервер можно использовать и просто JSON, желательно с указанием кодировки в заголовке Content-Type .
В XMLHttpRequest можно использовать и другие HTTP-методы, например PUT, DELETE, TRACE. К ним применимы все те же принципы, что описаны выше.