Xhr send error

Метод XMLHttpRequest.send() отправляет запрос. Если запрос асинхронный (каким он является по умолчанию), то возврат из данного метода происходит сразу после отправления запроса. Если запрос синхронный, то метод возвращает управление только после получения ответа. Метод send() принимает необязательные аргументы в тело запросов. Если метод запроса GET или HEAD, то аргументы игнорируются и тело запроса устанавливается в null.

Метод XMLHttpRequest.send() отправляет запрос. Если запрос асинхронный (каким он является по умолчанию), то возврат из данного метода происходит сразу после отправления запроса. Если запрос синхронный, то метод возвращает управление только после получения ответа. Метод send() принимает необязательные аргументы в тело запросов. Если метод запроса GET или HEAD, то аргументы игнорируются и тело запроса устанавливается в null.

Если заголовок Accept не был задан с помощью setRequestHeader(), будет отправлено значение Accept по умолчанию */*.

Синтаксис

XMLHttpRequest.send(body)

Параметры

body Необязательный

Данные из параметра body оправляются в запросе через XHR. Это могут быть:

  • Document, и в этом случае данные будут сериализованы перед отправкой.
  • BodyInit, которые, согласно спецификации Fetch могут быть: Blob, BufferSource, FormData, URLSearchParams, ReadableStream (en-US), или объектом USVString.

Лучший способ передать двоичные данные (например при загрузке файлов) — это использование ArrayBufferView (en-US) или Blobs в сочетании с методомsend().

Если никакого значения не определено в качестве body, то будет использовано значение по умолчанию: null.

Возвращаемое значение

undefined.

Исключения

Исключение Описание
InvalidStateError send() уже был вызван для запроса, и/или запрос завершён.
NetworkError Тип запрошенного ресурса — Blob, но метод запроса не GET.

Пример: GET

var xhr = new XMLHttpRequest();
xhr.open('GET', '/server', true);

xhr.onload = function () {
  // Запрос завершён. Здесь можно обрабатывать результат.
};

xhr.send(null);
// xhr.send('string');
// xhr.send(new Blob());
// xhr.send(new Int8Array());
// xhr.send({ form: 'data' });
// xhr.send(document);

Пример: POST

var xhr = new XMLHttpRequest();
xhr.open("POST", '/server', true);

//Передаёт правильный заголовок в запросе
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

xhr.onreadystatechange = function() {//Вызывает функцию при смене состояния.
    if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
        // Запрос завершён. Здесь можно обрабатывать результат.
    }
}
xhr.send("foo=bar&lorem=ipsum");
// xhr.send('string');
// xhr.send(new Blob());
// xhr.send(new Int8Array());
// xhr.send({ form: 'data' });
// xhr.send(document);

Спецификации

Specification
XMLHttpRequest Standard
# the-send()-method

Поддержка браузерами

BCD tables only load in the browser

Смотрите также

XMLHttpRequest is a built-in browser object in all modern browsers that can be used to make HTTP requests in JavaScript to exchange data between the web browser and the server.

Despite the word «XML» in its name, XMLHttpRequest can be used to retrieve any kind of data and not just XML. We can use it to upload/download files, submit form data, track progress, and much more.

Basic XHR Request

To send an HTTP request using XHR, create an XMLHttpRequest object, open a connection to the URL, and send the request. Once the request completes, the object will contain information such as the response body and the HTTP status code.

Let’s use JSONPlaceholder to test REST API to send a GET request using XHR:

// create an XHR object
const xhr = new XMLHttpRequest()

// listen for `onload` event
xhr.onload = () => {
  // process response
  if (xhr.status == 200) {
    // parse JSON data
    console.log(JSON.parse(xhr.response))
  } else {
    console.error('Error!')
  }
}

// create a `GET` request
xhr.open('GET', 'https://jsonplaceholder.typicode.com/users')

// send request
xhr.send()

The xhr.onload event only works in modern browsers (IE10+, Firefox, Chrome, Safari). If you want to support old browsers, use the xhr.onreadystatechange event instead.

xhr.open() Method

In the example above, we passed the HTTP method and a URL to the request to the open() method. This method is normally called right after new XMLHttpRequest(). We can use this method to specify the main parameters of the request:

Here is the syntax of this method:

xhr.open(method, URL, [async, user, password])
  • method — HTTP request method. It can be GET, POST, DELETE, PUT, etc.
  • URL — The URL to request, a string or a URL object
  • asnyc — Specify whether the request should be made asynchronously or not. The default value is true
  • username & password — Credentials for basic HTTP authentication

The open() method does not open the connection to the URL. It only configures the HTTP request.

xhr.send() Method

xhr.send([body])

The send() method opens the network connection and sends the request to the server. It takes an optional body parameter that contains the request body. For request methods like GET you do not need to pass the body parameter.

XHR Events

The three most widely used XHR events are the following:

  • load — This event is invoked when the result is ready. It is equivalent to the xhr.onreadystatechange event with xhr.readyState == 4.
  • error — This event is fired when the request is failed due to a network down or invalid URL.
  • progress — This event is triggered periodically during the response download. It can be used to report progress for large network requests.
// listen for `load` event
xhr.onload = () => {
  console.log(`Data Loaded: ${xhr.status} ${xhr.response}`)
}

// listen for `error` event
xhr.onerror = () => {
  console.error('Request failed.')
}

// listen for `progress` event
xhr.onprogress = event => {
  // event.loaded returns how many bytes are downloaded
  // event.total returns the total number of bytes
  // event.total is only available if server sends `Content-Length` header
  console.log(`Downloaded ${event.loaded} of ${event.total}`)
}

Request Timeout

You can easily configure the request timeout by specifying the time in milliseconds:

// set timeout
xhr.timeout = 5000 // 5 seconds

// listen for `timeout` event
xhr.ontimeout = () => console.log('Request timeout.', xhr.responseURL)

xhr.responseURL property returns the final URL of an XMLHttpRequest instance after following all redirects. This is the only way to retrieve the Location header.

Response Type

We can use the xhr.responseType property to set the expected response format:

  • Empty (default) or text — plain text
  • json — parsed JSON
  • blob — binary data Blob
  • document — XML document
  • arraybufferArrayBuffer for binary data

Let’s call a RESTful API to get the response as JSON:

const xhr = new XMLHttpRequest()

xhr.open('GET', 'https://api.jsonbin.io/b/5d5076e01ec3937ed4d05eab/1')

// set response format
xhr.responseType = 'json'

xhr.send()

xhr.onload = () => {
  // get JSON response
  const user = xhr.response

  // log details
  console.log(user.name) // John Doe
  console.log(user.email) // john.doe@example.com
  console.log(user.website) // http://example.com
}

Request States (xhr.readyState)

The XMLHttpRequest object changes state as the request progresses. We can access the current state using the xhr.readyState property.

The states are:

  • UNSENT (0) — The initial state
  • OPENED (1) — The request begins
  • HEADERS_RECEIVED (2) — The HTTP headers received
  • LOADING (3) — Response is loading
  • DONE (4) — The request is completed

We can track the request state by using the onreadystatechange event:

xhr.onreadystatechange = function () {
  if (xhr.readyState == 1) {
    console.log('Request started.')
  }

  if (xhr.readyState == 2) {
    console.log('Headers received.')
  }

  if (xhr.readyState == 3) {
    console.log('Data loading..!')
  }
  if (xhr.readyState == 4) {
    console.log('Request ended.')
  }
}

Aborting Request

We can easily abort an XHR request anytime by calling the abort() method on the xhr object:

xhr.abort() // cancel request

Synchronous Requests

By default, XHR makes an asynchronous request which is good for performance. But if you want to make an explicit synchronous request, just pass false as 3rd argument to the open() method. It will pause the JavaScript execution at send() and resume when the response is available:

xhr.open('GET', 'https://api.jsonbin.io/b/5d5076e01ec3937ed4d05eab/1', false)

Be careful! Chrome display the following warning for synchronous XHR request: [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects on the end user’s experience.

XMLHttpRequest allows us to set request headers and read response headers. We can set the request Content-Type & Accept headers by calling setRequestHeader() method on the xhr object:

// set request headers
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.setRequestHeader('Accept', '*/*') // accept all

Similarly, if you want to read the response headers (except Set-Cookie), call get response header() on the xhr object:

// read response headers
xhr.getResponseHeader('Content-Type')
xhr.getResponseHeader('Cache-Control')

Want to get response headers at once? Use getAllResponseHeaders() instead:

xhr.getAllResponseHeaders()

XHR POST Request

There are two ways to make a POST HTTP request using XMLHttpRequest: URL encoded form-data and FormData API.

XHR POST Request with with application/x-www-form-urlencoded

The following example demonstrates how you can make a POST request with URL-encoded form data:

const xhr = new XMLHttpRequest()

// configure a `POST` request
xhr.open('POST', '/login')

// prepare form data
let params = 'username=attacomsian&password=123456'

// set `Content-Type` header
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')

// pass `params` to `send()` method
xhr.send(params)

// listen for `load` event
xhr.onload = () => {
  console.log(xhr.responseText)
}

XHR POST Request with JSON Data

To make an XHR POST request with JSON data, you must the JSON data into a string using JSON.stringify() and set the content-type header to application/json:

const xhr = new XMLHttpRequest()

// configure a `POST` request
xhr.open('POST', '/login')

// create a JSON object
const params = {
  username: 'attacomsian',
  password: '123456'
}

// set `Content-Type` header
xhr.setRequestHeader('Content-Type', 'application/json')

// pass `params` to `send()` method
xhr.send(JSON.stringify(params))

// listen for `load` event
xhr.onload = () => {
  console.log(xhr.responseText)
}

Cross-Origin Requests & Cookies

XMLHttpRequest can send cross-origin requests, but it is subjected to special security measures. To request a resource from a different server, the server must explicitly support this using CORS (Cross-Origin Resource Sharing).

Just like Fetch API, XHR does not send cookies and HTTP authorization to another origin. To send cookies, you can use the withCredentials property of the xhr object:

xhr.withCredentials = true

XHR vs. jQuery

jQuery wrapper methods like $.ajax() use XHR under the hood to provide a higher level of abstraction. Using jQuery, we can translate the above code into just a few lines:

$.ajax('https://jsonplaceholder.typicode.com/users')
  .done(data => {
    console.log(data)
  })
  .fail(err => {
    console.error('Error:', err)
  })

XHR vs. Fetch API

The Fetch API is a promise-based modern alternative to XHR. It is clean, easier to understand, and massively used in PWA Service Workers.

The XHR example above can be converted to a much simpler fetch()-based code that even automatically parses the returned JSON:

fetch('https://jsonplaceholder.typicode.com/users')
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('Error:', err))

Read JavaScript Fetch API guide to understand how you can use Fetch API to request network resources with just a few lines of code.

✌️ Like this article? Follow me on
Twitter
and LinkedIn.
You can also subscribe to
RSS Feed.

I wrote a full solution to that problem. It works perfectly!
I have a function called networkOrfail which will try to resend the XMLHttpRequest each second, if the network is available. Otherwise, it’ll ignore the request.
When the request is succeeded, that polling stops and the response is returned.

Here’s how to detect whether the network is available:

function getNavigatorConection() {
    return navigator.onLine;
}

Then, create your XMLHttpRequest:

function makeRequest() {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', 'anypage/anotherpage', true);
    xhr.timeout = 2000;
    xhr.onload = function () {
        // Your request is completed
        if (xhr.readyState == 4 && xhr.status == 200) {
            // You're in a successfully condition
        }
    };
    xhr.ontimeout = function (e) {
        // Your request timed out
    };
    xhr.send(null);
}

Now, define your polling method as follows:

function networkOrFail(callFunc, callTime) {
    let connected = getNavigatorConection();
    let callableTimes = callTime < 2000 ? 2000 : callTime;
    let toursBegin = 3;
    let tours = toursBegin;
    let intervalId;
    let request = function() {
        intervalId = setInterval(function() { 
        let connected = getNavigatorConection();
        if (tours > 0) {
            if (connected) {
                callFunc();
                tours =0;
                return false;
            }
            tours--;
            alert("i tryied againt to resend for another time and it remain just "+tours+" to retry");
        } else {
            clearRequest();
            tours =toursBegin;
        }
    }, callableTimes > 5000 ? 5000 : callableTimes);

    };
    let clearRequest = function() {
        clearInterval(intervalId);
        intervalId = null;
    };
    if (connected)
        callFunc();
    else
        request();
}

Finally, call the send method through the polling method by passing it toghether with the timeout in minutes:

networkOrFail(makeRequest, 5000);

XMLHttpRequest

XMLHttpRequest — это встроенный в браузер объект, который даёт возможность делать HTTP-запросы к серверу без перезагрузки страницы.

Несмотря на наличие слова «XML» в названии, XMLHttpRequest может работать с любыми данными, а не только с XML. Мы можем загружать/скачивать файлы, отслеживать прогресс и многое другое.

На сегодняшний день не обязательно использовать XMLHttpRequest, так как существует другой, более современный метод fetch.

В современной веб-разработке XMLHttpRequest используется по трём причинам:

  1. По историческим причинам: существует много кода, использующего XMLHttpRequest, который нужно поддерживать.
  2. Необходимость поддерживать старые браузеры и нежелание использовать полифилы (например, чтобы уменьшить количество кода).
  3. Потребность в функциональности, которую fetch пока что не может предоставить, к примеру, отслеживание прогресса отправки на сервер.

Что-то из этого списка звучит знакомо? Если да, тогда вперёд, приятного знакомства с XMLHttpRequest. Если же нет, возможно, имеет смысл изучать сразу info:fetch.

Основы

XMLHttpRequest имеет два режима работы: синхронный и асинхронный.

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

Чтобы сделать запрос, нам нужно выполнить три шага:

  1. Создать XMLHttpRequest.

    let xhr = new XMLHttpRequest(); // у конструктора нет аргументов

    Конструктор не имеет аргументов.

  2. Инициализировать его.

    xhr.open(method, URL, [async, user, password])

    Этот метод обычно вызывается сразу после new XMLHttpRequest. В него передаются основные параметры запроса:

    • method — HTTP-метод. Обычно это "GET" или "POST".
    • URL — URL, куда отправляется запрос: строка, может быть и объект URL.
    • async — если указать false, тогда запрос будет выполнен синхронно, это мы рассмотрим чуть позже.
    • user, password — логин и пароль для базовой HTTP-авторизации (если требуется).

    Заметим, что вызов open, вопреки своему названию, не открывает соединение. Он лишь конфигурирует запрос, но непосредственно отсылается запрос только лишь после вызова send.

  3. Послать запрос.

    Этот метод устанавливает соединение и отсылает запрос к серверу. Необязательный параметр body содержит тело запроса.

    Некоторые типы запросов, такие как GET, не имеют тела. А некоторые, как, например, POST, используют body, чтобы отправлять данные на сервер. Мы позже увидим примеры.

  4. Слушать события на xhr, чтобы получить ответ.

    Три наиболее используемых события:

    • load — происходит, когда получен какой-либо ответ, включая ответы с HTTP-ошибкой, например 404.
    • error — когда запрос не может быть выполнен, например, нет соединения или невалидный URL.
    • progress — происходит периодически во время загрузки ответа, сообщает о прогрессе.
    xhr.onload = function() {
      alert(`Загружено: ${xhr.status} ${xhr.response}`);
    };
    
    xhr.onerror = function() { // происходит, только когда запрос совсем не получилось выполнить
      alert(`Ошибка соединения`);
    };
    
    xhr.onprogress = function(event) { // запускается периодически
      // event.loaded - количество загруженных байт
      // event.lengthComputable = равно true, если сервер присылает заголовок Content-Length
      // event.total - количество байт всего (только если lengthComputable равно true)
      alert(`Загружено ${event.loaded} из ${event.total}`);
    };

Вот полный пример. Код ниже загружает /article/xmlhttprequest/example/load с сервера и сообщает о прогрессе:

// 1. Создаём новый XMLHttpRequest-объект
let xhr = new XMLHttpRequest();

// 2. Настраиваем его: GET-запрос по URL /article/.../load
xhr.open('GET', '/article/xmlhttprequest/example/load');

// 3. Отсылаем запрос
xhr.send();

// 4. Этот код сработает после того, как мы получим ответ сервера
xhr.onload = function() {
  if (xhr.status != 200) { // анализируем HTTP-статус ответа, если статус не 200, то произошла ошибка
    alert(`Ошибка ${xhr.status}: ${xhr.statusText}`); // Например, 404: Not Found
  } else { // если всё прошло гладко, выводим результат
    alert(`Готово, получили ${xhr.response.length} байт`); // response -- это ответ сервера
  }
};

xhr.onprogress = function(event) {
  if (event.lengthComputable) {
    alert(`Получено ${event.loaded} из ${event.total} байт`);
  } else {
    alert(`Получено ${event.loaded} байт`); // если в ответе нет заголовка Content-Length
  }

};

xhr.onerror = function() {
  alert("Запрос не удался");
};

После ответа сервера мы можем получить результат запроса в следующих свойствах xhr:

status
: Код состояния HTTP (число): 200, 404, 403 и так далее, может быть 0 в случае, если ошибка не связана с HTTP.

statusText
: Сообщение о состоянии ответа HTTP (строка): обычно OK для 200, Not Found для 404, Forbidden для 403, и так далее.

response (в старом коде может встречаться как responseText)
: Тело ответа сервера.

Мы можем также указать таймаут — промежуток времени, который мы готовы ждать ответ:

xhr.timeout = 10000; // таймаут указывается в миллисекундах, т.е. 10 секунд

Если запрос не успевает выполниться в установленное время, то он прерывается, и происходит событие timeout.

Чтобы добавить к URL параметры, вида `?name=value`, и корректно закодировать их, можно использовать объект [URL](info:url):

```js
let url = new URL('https://google.com/search');
url.searchParams.set('q', 'test me!');

// параметр 'q' закодирован
xhr.open('GET', url); // https://google.com/search?q=test+me%21
```

Тип ответа

Мы можем использовать свойство xhr.responseType, чтобы указать ожидаемый тип ответа:

  • "" (по умолчанию) — строка,
  • "text" — строка,
  • "arraybuffer"ArrayBuffer (для бинарных данных, смотрите в info:arraybuffer-binary-arrays),
  • "blob"Blob (для бинарных данных, смотрите в info:blob),
  • "document" — XML-документ (может использовать XPath и другие XML-методы),
  • "json" — JSON (парсится автоматически).

К примеру, давайте получим ответ в формате JSON:

let xhr = new XMLHttpRequest();

xhr.open('GET', '/article/xmlhttprequest/example/json');

*!*
xhr.responseType = 'json';
*/!*

xhr.send();

// тело ответа {"сообщение": "Привет, мир!"}
xhr.onload = function() {
  let responseObj = xhr.response;
  alert(responseObj.message); // Привет, мир!
};
В старом коде вы можете встретить свойства `xhr.responseText` и даже `xhr.responseXML`.

Они существуют по историческим причинам, раньше с их помощью получали строки или XML-документы. Сегодня следует устанавливать желаемый тип объекта в `xhr.responseType` и получать `xhr.response`, как показано выше.

Состояния запроса

У XMLHttpRequest есть состояния, которые меняются по мере выполнения запроса. Текущее состояние можно посмотреть в свойстве xhr.readyState.

Список всех состояний, указанных в спецификации:

UNSENT = 0; // исходное состояние
OPENED = 1; // вызван метод open
HEADERS_RECEIVED = 2; // получены заголовки ответа
LOADING = 3; // ответ в процессе передачи (данные частично получены)
DONE = 4; // запрос завершён

Состояния объекта XMLHttpRequest меняются в таком порядке: 0 -> 1 -> 2 -> 3 -> … -> 3 -> 4. Состояние 3 повторяется каждый раз, когда получена часть данных.

Изменения в состоянии объекта запроса генерируют событие readystatechange:

xhr.onreadystatechange = function() {
  if (xhr.readyState == 3) {
    // загрузка
  }
  if (xhr.readyState == 4) {
    // запрос завершён
  }
};

Вы можете наткнуться на обработчики события readystatechange в очень старом коде, так уж сложилось исторически, когда-то не было событий load и других. Сегодня из-за существования событий load/error/progress можно сказать, что событие readystatechange «морально устарело».

Отмена запроса

Если мы передумали делать запрос, можно отменить его вызовом xhr.abort():

xhr.abort(); // завершить запрос

При этом генерируется событие abort, а xhr.status устанавливается в 0.

Синхронные запросы

Если в методе open третий параметр async установлен на false, запрос выполняется синхронно.

Другими словами, выполнение JavaScript останавливается на send() и возобновляется после получения ответа. Так ведут себя, например, функции alert или prompt.

Вот переписанный пример с параметром async, равным false:

let xhr = new XMLHttpRequest();

xhr.open('GET', '/article/xmlhttprequest/hello.txt', *!*false*/!*);

try {
  xhr.send();
  if (xhr.status != 200) {
    alert(`Ошибка ${xhr.status}: ${xhr.statusText}`);
  } else {
    alert(xhr.response);
  }
} catch(err) { // для отлова ошибок используем конструкцию try...catch вместо onerror
  alert("Запрос не удался");
}

Выглядит, может быть, и неплохо, но синхронные запросы используются редко, так как они блокируют выполнение JavaScript до тех пор, пока загрузка не завершена. В некоторых браузерах нельзя прокручивать страницу, пока идёт синхронный запрос. Ну а если же синхронный запрос по какой-то причине выполняется слишком долго, браузер предложит закрыть «зависшую» страницу.

Многие продвинутые возможности XMLHttpRequest, такие как выполнение запроса на другой домен или установка таймаута, недоступны для синхронных запросов. Также, как вы могли заметить, ни о какой индикации прогресса речь тут не идёт.

Из-за всего этого синхронные запросы используют очень редко. Мы более не будем рассматривать их.

HTTP-заголовки

XMLHttpRequest умеет как указывать свои заголовки в запросе, так и читать присланные в ответ.

Для работы с HTTP-заголовками есть 3 метода:

setRequestHeader(name, value)
: Устанавливает заголовок запроса с именем name и значением value.

Например:

```js
xhr.setRequestHeader('Content-Type', 'application/json');
```

```warn header="Ограничения на заголовки"
Некоторые заголовки управляются исключительно браузером, например `Referer` или `Host`, а также ряд других.
Полный список [тут](https://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader-method).

`XMLHttpRequest` не разрешено изменять их ради безопасности пользователей и для обеспечения корректности HTTP-запроса.
```

````warn header="Поставленный заголовок нельзя снять"
Ещё одной особенностью `XMLHttpRequest` является то, что отменить `setRequestHeader` невозможно.

Если заголовок определён, то его нельзя снять. Повторные вызовы лишь добавляют информацию к заголовку, а не перезаписывают его.

Например:

```js
xhr.setRequestHeader('X-Auth', '123');
xhr.setRequestHeader('X-Auth', '456');

// заголовок получится такой:
// X-Auth: 123, 456
```
````

getResponseHeader(name)
: Возвращает значение заголовка ответа name (кроме Set-Cookie и Set-Cookie2).

Например:

```js
xhr.getResponseHeader('Content-Type')
```

getAllResponseHeaders()
: Возвращает все заголовки ответа, кроме Set-Cookie и Set-Cookie2.

Заголовки возвращаются в виде единой строки, например:

```http
Cache-Control: max-age=31536000
Content-Length: 4260
Content-Type: image/png
Date: Sat, 08 Sep 2012 16:53:16 GMT
```

Между заголовками всегда стоит перевод строки в два символа `"rn"` (независимо от ОС), так что мы можем легко разделить их на отдельные заголовки. Значение заголовка всегда отделено двоеточием с пробелом `": "`. Этот формат задан стандартом.

Таким образом, если хочется получить объект с парами заголовок-значение, нам нужно задействовать немного JS.

Вот так (предполагается, что если два заголовка имеют одинаковое имя, то последний перезаписывает предыдущий):

```js
let headers = xhr
  .getAllResponseHeaders()
  .split('rn')
  .reduce((result, current) => {
    let [name, value] = current.split(': ');
    result[name] = value;
    return result;
  }, {});

// headers['Content-Type'] = 'image/png'
```

POST, FormData

Чтобы сделать POST-запрос, мы можем использовать встроенный объект FormData.

Синтаксис:

let formData = new FormData([form]); // создаём объект, по желанию берём данные формы <form>
formData.append(name, value); // добавляем поле

Мы создаём объект, при желании указываем, из какой формы form взять данные, затем, если нужно, с помощью метода append добавляем дополнительные поля, после чего:

  1. xhr.open('POST', ...) – создаём POST-запрос.
  2. xhr.send(formData) – отсылаем форму серверу.

Например:

<form name="person">
  <input name="name" value="Петя">
  <input name="surname" value="Васечкин">
</form>

<script>
  // заполним FormData данными из формы
  let formData = new FormData(document.forms.person);

  // добавим ещё одно поле
  formData.append("middle", "Иванович");

  // отправим данные
  let xhr = new XMLHttpRequest();
  xhr.open("POST", "/article/xmlhttprequest/post/user");
  xhr.send(formData);

  xhr.onload = () => alert(xhr.response);
</script>

Обычно форма отсылается в кодировке multipart/form-data.

Если нам больше нравится формат JSON, то используем JSON.stringify и отправляем данные как строку.

Важно не забыть поставить соответствующий заголовок Content-Type: application/json, многие серверные фреймворки автоматически декодируют JSON при его наличии:

let xhr = new XMLHttpRequest();

let json = JSON.stringify({
  name: "Вася",
  surname: "Петров"
});

xhr.open("POST", '/submit')
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');

xhr.send(json);

Метод .send(body) весьма всеяден. Он может отправить практически что угодно в body, включая объекты типа Blob и BufferSource.

Прогресс отправки

Событие progress срабатывает только на стадии загрузки ответа с сервера.

А именно: если мы отправляем что-то через POST-запрос, XMLHttpRequest сперва отправит наши данные (тело запроса) на сервер, а потом загрузит ответ сервера. И событие progress будет срабатывать только во время загрузки ответа.

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

Существует другой объект, без методов, только для отслеживания событий отправки: xhr.upload.

Он генерирует события, похожие на события xhr, но только во время отправки данных на сервер:

  • loadstart — начало загрузки данных.
  • progress — генерируется периодически во время отправки на сервер.
  • abort — загрузка прервана.
  • error — ошибка, не связанная с HTTP.
  • load — загрузка успешно завершена.
  • timeout — вышло время, отведённое на загрузку (при установленном свойстве timeout).
  • loadend — загрузка завершена, вне зависимости от того, как — успешно или нет.

Примеры обработчиков для этих событий:

xhr.upload.onprogress = function(event) {
  alert(`Отправлено ${event.loaded} из ${event.total} байт`);
};

xhr.upload.onload = function() {
  alert(`Данные успешно отправлены.`);
};

xhr.upload.onerror = function() {
  alert(`Произошла ошибка во время отправки: ${xhr.status}`);
};

Пример из реальной жизни: загрузка файла на сервер с индикацией прогресса:

<input type="file" onchange="upload(this.files[0])">

<script>
function upload(file) {
  let xhr = new XMLHttpRequest();

  // отслеживаем процесс отправки
*!*
  xhr.upload.onprogress = function(event) {
    console.log(`Отправлено ${event.loaded} из ${event.total}`);
  };
*/!*

  // Ждём завершения: неважно, успешного или нет
  xhr.onloadend = function() {
    if (xhr.status == 200) {
      console.log("Успех");
    } else {
      console.log("Ошибка " + this.status);
    }
  };

  xhr.open("POST", "/article/xmlhttprequest/post/upload");
  xhr.send(file);
}
</script>

Запросы на другой источник

XMLHttpRequest может осуществлять запросы на другие сайты, используя ту же политику CORS, что и fetch.

Точно так же, как и при работе с fetch, по умолчанию на другой источник не отсылаются куки и заголовки HTTP-авторизации. Чтобы это изменить, установите xhr.withCredentials в true:

let xhr = new XMLHttpRequest();
*!*
xhr.withCredentials = true;
*/!*

xhr.open('POST', 'http://anywhere.com/request');
...

Детали по заголовкам, которые при этом необходимы, смотрите в главе fetch.

Итого

Типичный код GET-запроса с использованием XMLHttpRequest:

let xhr = new XMLHttpRequest();

xhr.open('GET', '/my/url');

xhr.send();

xhr.onload = function() {
  if (xhr.status != 200) { // HTTP ошибка?
    // обработаем ошибку
    alert( 'Ошибка: ' + xhr.status);
    return;
  }

  // получим ответ из xhr.response
};

xhr.onprogress = function(event) {
  // выведем прогресс
  alert(`Загружено ${event.loaded} из ${event.total}`);
};

xhr.onerror = function() {
  // обработаем ошибку, не связанную с HTTP (например, нет соединения)
};

Событий на самом деле больше, в современной спецификации они все перечислены в том порядке, в каком генерируются во время запроса:

  • loadstart — начало запроса.
  • progress — прибыла часть данных ответа, тело ответа полностью на данный момент можно получить из свойства responseText.
  • abort — запрос был прерван вызовом xhr.abort().
  • error — произошла ошибка соединения, например неправильное доменное имя. Событие не генерируется для HTTP-ошибок как, например, 404.
  • load — запрос успешно завершён.
  • timeout — запрос был отменён по причине истечения отведённого для него времени (происходит, только если был установлен таймаут).
  • loadend — срабатывает после load, error, timeout или abort.

События error, abort, timeout и load взаимно исключают друг друга — может произойти только одно из них.

Наиболее часто используют события завершения загрузки (load), ошибки загрузки (error), или мы можем использовать единый обработчик loadend для всего и смотреть в свойствах объекта запроса xhr детали произошедшего.

Также мы уже видели событие: readystatechange. Исторически оно появилось одним из первых, даже раньше, чем была составлена спецификация. Сегодня нет необходимости использовать его, так как оно может быть заменено современными событиями, но на него можно часто наткнуться в старом коде.

Если же нам нужно следить именно за процессом отправки данных на сервер, тогда можно использовать те же события, но для объекта xhr.upload.

Содержание

  1. XMLHttpRequest
  2. Основы
  3. Тип ответа
  4. Состояния запроса
  5. Отмена запроса
  6. Синхронные запросы
  7. HTTP-заголовки
  8. POST, FormData
  9. Прогресс отправки
  10. Запросы на другой источник
  11. Итого
  12. XMLHttpRequest
  13. The basics
  14. Response Type
  15. Ready states
  16. Aborting request
  17. Synchronous requests
  18. HTTP-headers
  19. POST, FormData
  20. Upload progress
  21. Cross-origin requests
  22. Summary

XMLHttpRequest

XMLHttpRequest – это встроенный в браузер объект, который даёт возможность делать HTTP-запросы к серверу без перезагрузки страницы.

Несмотря на наличие слова «XML» в названии, XMLHttpRequest может работать с любыми данными, а не только с XML. Мы можем загружать/скачивать файлы, отслеживать прогресс и многое другое.

На сегодняшний день не обязательно использовать XMLHttpRequest , так как существует другой, более современный метод fetch .

В современной веб-разработке XMLHttpRequest используется по трём причинам:

  1. По историческим причинам: существует много кода, использующего XMLHttpRequest , который нужно поддерживать.
  2. Необходимость поддерживать старые браузеры и нежелание использовать полифилы (например, чтобы уменьшить количество кода).
  3. Потребность в функциональности, которую fetch пока что не может предоставить, к примеру, отслеживание прогресса отправки на сервер.

Что-то из этого списка звучит знакомо? Если да, тогда вперёд, приятного знакомства с XMLHttpRequest . Если же нет, возможно, имеет смысл изучать сразу Fetch.

Основы

XMLHttpRequest имеет два режима работы: синхронный и асинхронный.

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

Чтобы сделать запрос, нам нужно выполнить три шага:

Конструктор не имеет аргументов.

Этот метод обычно вызывается сразу после new XMLHttpRequest . В него передаются основные параметры запроса:

  • method – HTTP-метод. Обычно это «GET» или «POST» .
  • URL – URL, куда отправляется запрос: строка, может быть и объект URL.
  • async – если указать false , тогда запрос будет выполнен синхронно, это мы рассмотрим чуть позже.
  • user , password – логин и пароль для базовой HTTP-авторизации (если требуется).

Заметим, что вызов open , вопреки своему названию, не открывает соединение. Он лишь конфигурирует запрос, но непосредственно отсылается запрос только лишь после вызова send .

Этот метод устанавливает соединение и отсылает запрос к серверу. Необязательный параметр body содержит тело запроса.

Некоторые типы запросов, такие как GET , не имеют тела. А некоторые, как, например, POST , используют body , чтобы отправлять данные на сервер. Мы позже увидим примеры.

Слушать события на xhr , чтобы получить ответ.

Три наиболее используемых события:

  • load – происходит, когда получен какой-либо ответ, включая ответы с HTTP-ошибкой, например 404.
  • error – когда запрос не может быть выполнен, например, нет соединения или невалидный URL.
  • progress – происходит периодически во время загрузки ответа, сообщает о прогрессе.

Вот полный пример. Код ниже загружает /article/xmlhttprequest/example/load с сервера и сообщает о прогрессе:

После ответа сервера мы можем получить результат запроса в следующих свойствах xhr :

status Код состояния HTTP (число): 200 , 404 , 403 и так далее, может быть 0 в случае, если ошибка не связана с HTTP. statusText Сообщение о состоянии ответа HTTP (строка): обычно OK для 200 , Not Found для 404 , Forbidden для 403 , и так далее. response (в старом коде может встречаться как responseText ) Тело ответа сервера.

Мы можем также указать таймаут – промежуток времени, который мы готовы ждать ответ:

Если запрос не успевает выполниться в установленное время, то он прерывается, и происходит событие timeout .

Чтобы добавить к URL параметры, вида ?name=value , и корректно закодировать их, можно использовать объект URL:

Тип ответа

Мы можем использовать свойство xhr.responseType , чтобы указать ожидаемый тип ответа:

  • «» (по умолчанию) – строка,
  • «text» – строка,
  • «arraybuffer» – ArrayBuffer (для бинарных данных, смотрите в ArrayBuffer, бинарные массивы),
  • «blob» – Blob (для бинарных данных, смотрите в Blob),
  • «document» – XML-документ (может использовать XPath и другие XML-методы),
  • «json» – JSON (парсится автоматически).

К примеру, давайте получим ответ в формате JSON:

В старом коде вы можете встретить свойства xhr.responseText и даже xhr.responseXML .

Они существуют по историческим причинам, раньше с их помощью получали строки или XML-документы. Сегодня следует устанавливать желаемый тип объекта в xhr.responseType и получать xhr.response , как показано выше.

Состояния запроса

У XMLHttpRequest есть состояния, которые меняются по мере выполнения запроса. Текущее состояние можно посмотреть в свойстве xhr.readyState .

Список всех состояний, указанных в спецификации:

Состояния объекта XMLHttpRequest меняются в таком порядке: 0 → 1 → 2 → 3 → … → 3 → 4 . Состояние 3 повторяется каждый раз, когда получена часть данных.

Изменения в состоянии объекта запроса генерируют событие readystatechange :

Вы можете наткнуться на обработчики события readystatechange в очень старом коде, так уж сложилось исторически, когда-то не было событий load и других. Сегодня из-за существования событий load/error/progress можно сказать, что событие readystatechange «морально устарело».

Отмена запроса

Если мы передумали делать запрос, можно отменить его вызовом xhr.abort() :

При этом генерируется событие abort , а xhr.status устанавливается в 0 .

Синхронные запросы

Если в методе open третий параметр async установлен на false , запрос выполняется синхронно.

Другими словами, выполнение JavaScript останавливается на send() и возобновляется после получения ответа. Так ведут себя, например, функции alert или prompt .

Вот переписанный пример с параметром async , равным false :

Выглядит, может быть, и неплохо, но синхронные запросы используются редко, так как они блокируют выполнение JavaScript до тех пор, пока загрузка не завершена. В некоторых браузерах нельзя прокручивать страницу, пока идёт синхронный запрос. Ну а если же синхронный запрос по какой-то причине выполняется слишком долго, браузер предложит закрыть «зависшую» страницу.

Многие продвинутые возможности XMLHttpRequest , такие как выполнение запроса на другой домен или установка таймаута, недоступны для синхронных запросов. Также, как вы могли заметить, ни о какой индикации прогресса речь тут не идёт.

Из-за всего этого синхронные запросы используют очень редко. Мы более не будем рассматривать их.

HTTP-заголовки

XMLHttpRequest умеет как указывать свои заголовки в запросе, так и читать присланные в ответ.

Для работы с HTTP-заголовками есть 3 метода:

Устанавливает заголовок запроса с именем name и значением value .

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

XMLHttpRequest не разрешено изменять их ради безопасности пользователей и для обеспечения корректности HTTP-запроса.

Ещё одной особенностью XMLHttpRequest является то, что отменить setRequestHeader невозможно.

Если заголовок определён, то его нельзя снять. Повторные вызовы лишь добавляют информацию к заголовку, а не перезаписывают его.

Возвращает значение заголовка ответа name (кроме Set-Cookie и Set-Cookie2 ).

Возвращает все заголовки ответа, кроме Set-Cookie и Set-Cookie2 .

Заголовки возвращаются в виде единой строки, например:

Между заголовками всегда стоит перевод строки в два символа «rn» (независимо от ОС), так что мы можем легко разделить их на отдельные заголовки. Значение заголовка всегда отделено двоеточием с пробелом «: » . Этот формат задан стандартом.

Таким образом, если хочется получить объект с парами заголовок-значение, нам нужно задействовать немного JS.

Вот так (предполагается, что если два заголовка имеют одинаковое имя, то последний перезаписывает предыдущий):

POST, FormData

Чтобы сделать POST-запрос, мы можем использовать встроенный объект FormData.

Обычно форма отсылается в кодировке multipart/form-data .

Если нам больше нравится формат JSON, то используем JSON.stringify и отправляем данные как строку.

Важно не забыть поставить соответствующий заголовок Content-Type: application/json , многие серверные фреймворки автоматически декодируют JSON при его наличии:

Метод .send(body) весьма всеяден. Он может отправить практически что угодно в body , включая объекты типа Blob и BufferSource .

Прогресс отправки

Событие progress срабатывает только на стадии загрузки ответа с сервера.

А именно: если мы отправляем что-то через POST -запрос, XMLHttpRequest сперва отправит наши данные (тело запроса) на сервер, а потом загрузит ответ сервера. И событие progress будет срабатывать только во время загрузки ответа.

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

Существует другой объект, без методов, только для отслеживания событий отправки: xhr.upload .

Он генерирует события, похожие на события xhr , но только во время отправки данных на сервер:

  • loadstart – начало загрузки данных.
  • progress – генерируется периодически во время отправки на сервер.
  • abort – загрузка прервана.
  • error – ошибка, не связанная с HTTP.
  • load – загрузка успешно завершена.
  • timeout – вышло время, отведённое на загрузку (при установленном свойстве timeout ).
  • loadend – загрузка завершена, вне зависимости от того, как – успешно или нет.

Примеры обработчиков для этих событий:

Пример из реальной жизни: загрузка файла на сервер с индикацией прогресса:

Запросы на другой источник

XMLHttpRequest может осуществлять запросы на другие сайты, используя ту же политику CORS, что и fetch.

Точно так же, как и при работе с fetch , по умолчанию на другой источник не отсылаются куки и заголовки HTTP-авторизации. Чтобы это изменить, установите xhr.withCredentials в true :

Детали по заголовкам, которые при этом необходимы, смотрите в главе fetch.

Итого

Типичный код GET-запроса с использованием XMLHttpRequest :

Событий на самом деле больше, в современной спецификации они все перечислены в том порядке, в каком генерируются во время запроса:

  • loadstart – начало запроса.
  • progress – прибыла часть данных ответа, тело ответа полностью на данный момент можно получить из свойства responseText .
  • abort – запрос был прерван вызовом xhr.abort() .
  • error – произошла ошибка соединения, например неправильное доменное имя. Событие не генерируется для HTTP-ошибок как, например, 404.
  • load – запрос успешно завершён.
  • timeout – запрос был отменён по причине истечения отведённого для него времени (происходит, только если был установлен таймаут).
  • loadend – срабатывает после load , error , timeout или abort .

События error , abort , timeout и load взаимно исключают друг друга – может произойти только одно из них.

Наиболее часто используют события завершения загрузки ( load ), ошибки загрузки ( error ), или мы можем использовать единый обработчик loadend для всего и смотреть в свойствах объекта запроса xhr детали произошедшего.

Также мы уже видели событие: readystatechange . Исторически оно появилось одним из первых, даже раньше, чем была составлена спецификация. Сегодня нет необходимости использовать его, так как оно может быть заменено современными событиями, но на него можно часто наткнуться в старом коде.

Источник

XMLHttpRequest

XMLHttpRequest is a built-in browser object that allows to make HTTP requests in JavaScript.

Despite having the word “XML” in its name, it can operate on any data, not only in XML format. We can upload/download files, track progress and much more.

Right now, there’s another, more modern method fetch , that somewhat deprecates XMLHttpRequest .

In modern web-development XMLHttpRequest is used for three reasons:

  1. Historical reasons: we need to support existing scripts with XMLHttpRequest .
  2. We need to support old browsers, and don’t want polyfills (e.g. to keep scripts tiny).
  3. We need something that fetch can’t do yet, e.g. to track upload progress.

Does that sound familiar? If yes, then all right, go on with XMLHttpRequest . Otherwise, please head on to Fetch.

The basics

XMLHttpRequest has two modes of operation: synchronous and asynchronous.

Let’s see the asynchronous first, as it’s used in the majority of cases.

To do the request, we need 3 steps:

The constructor has no arguments.

Initialize it, usually right after new XMLHttpRequest :

This method specifies the main parameters of the request:

  • method – HTTP-method. Usually «GET» or «POST» .
  • URL – the URL to request, a string, can be URL object.
  • async – if explicitly set to false , then the request is synchronous, we’ll cover that a bit later.
  • user , password – login and password for basic HTTP auth (if required).

Please note that open call, contrary to its name, does not open the connection. It only configures the request, but the network activity only starts with the call of send .

This method opens the connection and sends the request to server. The optional body parameter contains the request body.

Some request methods like GET do not have a body. And some of them like POST use body to send the data to the server. We’ll see examples of that later.

Listen to xhr events for response.

These three events are the most widely used:

  • load – when the request is complete (even if HTTP status is like 400 or 500), and the response is fully downloaded.
  • error – when the request couldn’t be made, e.g. network down or invalid URL.
  • progress – triggers periodically while the response is being downloaded, reports how much has been downloaded.

Here’s a full example. The code below loads the URL at /article/xmlhttprequest/example/load from the server and prints the progress:

Once the server has responded, we can receive the result in the following xhr properties:

status HTTP status code (a number): 200 , 404 , 403 and so on, can be 0 in case of a non-HTTP failure. statusText HTTP status message (a string): usually OK for 200 , Not Found for 404 , Forbidden for 403 and so on. response (old scripts may use responseText ) The server response body.

We can also specify a timeout using the corresponding property:

If the request does not succeed within the given time, it gets canceled and timeout event triggers.

To add parameters to URL, like ?name=value , and ensure the proper encoding, we can use URL object:

Response Type

We can use xhr.responseType property to set the response format:

  • «» (default) – get as string,
  • «text» – get as string,
  • «arraybuffer» – get as ArrayBuffer (for binary data, see chapter ArrayBuffer, binary arrays),
  • «blob» – get as Blob (for binary data, see chapter Blob),
  • «document» – get as XML document (can use XPath and other XML methods) or HTML document (based on the MIME type of the received data),
  • «json» – get as JSON (parsed automatically).

For example, let’s get the response as JSON:

In the old scripts you may also find xhr.responseText and even xhr.responseXML properties.

They exist for historical reasons, to get either a string or XML document. Nowadays, we should set the format in xhr.responseType and get xhr.response as demonstrated above.

Ready states

XMLHttpRequest changes between states as it progresses. The current state is accessible as xhr.readyState .

An XMLHttpRequest object travels them in the order 0 → 1 → 2 → 3 → … → 3 → 4 . State 3 repeats every time a data packet is received over the network.

We can track them using readystatechange event:

You can find readystatechange listeners in really old code, it’s there for historical reasons, as there was a time when there were no load and other events. Nowadays, load/error/progress handlers deprecate it.

Aborting request

We can terminate the request at any time. The call to xhr.abort() does that:

That triggers abort event, and xhr.status becomes 0 .

Synchronous requests

If in the open method the third parameter async is set to false , the request is made synchronously.

In other words, JavaScript execution pauses at send() and resumes when the response is received. Somewhat like alert or prompt commands.

Here’s the rewritten example, the 3rd parameter of open is false :

It might look good, but synchronous calls are used rarely, because they block in-page JavaScript till the loading is complete. In some browsers it becomes impossible to scroll. If a synchronous call takes too much time, the browser may suggest to close the “hanging” webpage.

Many advanced capabilities of XMLHttpRequest , like requesting from another domain or specifying a timeout, are unavailable for synchronous requests. Also, as you can see, no progress indication.

Because of all that, synchronous requests are used very sparingly, almost never. We won’t talk about them any more.

XMLHttpRequest allows both to send custom headers and read headers from the response.

There are 3 methods for HTTP-headers:

Sets the request header with the given name and value .

Several headers are managed exclusively by the browser, e.g. Referer and Host . The full list is in the specification.

XMLHttpRequest is not allowed to change them, for the sake of user safety and correctness of the request.

Another peculiarity of XMLHttpRequest is that one can’t undo setRequestHeader .

Once the header is set, it’s set. Additional calls add information to the header, don’t overwrite it.

Gets the response header with the given name (except Set-Cookie and Set-Cookie2 ).

Returns all response headers, except Set-Cookie and Set-Cookie2 .

Headers are returned as a single line, e.g.:

The line break between headers is always «rn» (doesn’t depend on OS), so we can easily split it into individual headers. The separator between the name and the value is always a colon followed by a space «: » . That’s fixed in the specification.

So, if we want to get an object with name/value pairs, we need to throw in a bit JS.

Like this (assuming that if two headers have the same name, then the latter one overwrites the former one):

POST, FormData

To make a POST request, we can use the built-in FormData object.

The form is sent with multipart/form-data encoding.

Or, if we like JSON more, then JSON.stringify and send as a string.

Just don’t forget to set the header Content-Type: application/json , many server-side frameworks automatically decode JSON with it:

The .send(body) method is pretty omnivore. It can send almost any body , including Blob and BufferSource objects.

Upload progress

The progress event triggers only on the downloading stage.

That is: if we POST something, XMLHttpRequest first uploads our data (the request body), then downloads the response.

If we’re uploading something big, then we’re surely more interested in tracking the upload progress. But xhr.onprogress doesn’t help here.

There’s another object, without methods, exclusively to track upload events: xhr.upload .

It generates events, similar to xhr , but xhr.upload triggers them solely on uploading:

  • loadstart – upload started.
  • progress – triggers periodically during the upload.
  • abort – upload aborted.
  • error – non-HTTP error.
  • load – upload finished successfully.
  • timeout – upload timed out (if timeout property is set).
  • loadend – upload finished with either success or error.

Example of handlers:

Here’s a real-life example: file upload with progress indication:

Cross-origin requests

XMLHttpRequest can make cross-origin requests, using the same CORS policy as fetch.

Just like fetch , it doesn’t send cookies and HTTP-authorization to another origin by default. To enable them, set xhr.withCredentials to true :

See the chapter Fetch: Cross-Origin Requests for details about cross-origin headers.

Summary

Typical code of the GET-request with XMLHttpRequest :

There are actually more events, the modern specification lists them (in the lifecycle order):

  • loadstart – the request has started.
  • progress – a data packet of the response has arrived, the whole response body at the moment is in response .
  • abort – the request was canceled by the call xhr.abort() .
  • error – connection error has occurred, e.g. wrong domain name. Doesn’t happen for HTTP-errors like 404.
  • load – the request has finished successfully.
  • timeout – the request was canceled due to timeout (only happens if it was set).
  • loadend – triggers after load , error , timeout or abort .

The error , abort , timeout , and load events are mutually exclusive. Only one of them may happen.

The most used events are load completion ( load ), load failure ( error ), or we can use a single loadend handler and check the properties of the request object xhr to see what happened.

We’ve already seen another event: readystatechange . Historically, it appeared long ago, before the specification settled. Nowadays, there’s no need to use it, we can replace it with newer events, but it can often be found in older scripts.

Источник

Основы объекта XMLHttpRequest

Здравствуйте!  Продолжаем  разбираться с AJAX  и в  этом уроке я разберу пожалуй  главный объект без которого трудно себе представить  технологию AJAX —  XMLHttpRequest (или, сокращенно  «XHR»)  он дает возможность из JavaScript делать HTTP-запросы к серверу без перезагрузки страницы, то есть это и есть AJAX.

Несмотря на,  то что в  название присутствует  слово «XML», XMLHttpRequest может работать в принципе  с любыми данными, а не только с XML.

объект xmlhttprequest

Использовать его очень просто. Давайте рассмотрим примеры.

Пример использования

Как правило, XMLHttpRequest используется для загрузки данных.

Для начала рассмотрим пример использования, который загружает файл с телефонами  phones.json из текущей директории и выдаёт его содержимое:

// 1. Создаём новый объект XMLHttpRequest var xhr1 = new XMLHttpRequest(); 
// 2. Конфигурируем его: GET-запрос на URL 'phones.json' xhr1.open('GET', 'phones.json', false); 
// 3. Отсылаем запрос xhr1.send(); // 4. Если код ответа сервера не 200, то это ошибка if (xhr1.status != 200) { 
// обработать ошибку alert( xhr1.status + ': ' + xhr1.statusText ); 
// пример вывода: 404: Not Found } 
else { // вывести результат alert( xhr1.responseText ); // responseText -- текст ответа. }

Далее мы более подробно разберём основные методы и свойства объекта XMLHttpRequest.

Установить соединение: open

Синтаксис:

xhr.open(method, URL, async, user, password)

Этот метод –  вызывается первым после создания объекта XMLHttpRequest.

Задаёт основные параметры запроса:

  • method – HTTP-метод. Используется GET либо POST.
  • URL – адрес запроса. Можно использовать не только http/https, но и другие протоколы, например ftp:// и file://.При этом есть ограничения безопасности, называемые «Same Origin Policy»: запрос со страницы можно отправлять только на тот же протокол://домен:порт, с которого она пришла.
  • async – если установлено в false, то запрос производится синхронно, если true – асинхронно.

«Синхронный запрос» означает, что после вызова xhr.send() и до ответа сервера поток будет «заморожен»: посетитель не сможет взаимодействовать со страницей – прокручивать, нажимать на кнопки и т.п. После получения ответа выполнение продолжится со следующей строки.

«Асинхронный запрос» означает, что браузер отправит запрос, а далее результат нужно будет получить через обработчики событий.

  • user, password – логин и пароль для HTTP-авторизации, если нужны.

Вызов open не открывает соединение

Заметим, что вызов open, в противоположность своему названию (open – англ. «открыть») не открывает соединение. Он лишь настраивает запрос, а открытие соединения  инициируется методом send.

Отослать данные: send

Синтаксис:

xhr.send([body])

Именно этод метод открывает соединение и отправляет собственно запрос на сервер.

В body как раз  находится тело запроса. Не у всякого запроса есть тело, например у GET-запросов тела нет, а   вот у POST – основные данные как раз передаются через body.

Отмена: abort

Вызов xhr.abort() прерывает выполнение запроса. Если в  этом есть необходимость

Ответ: status, statusText, responseText

Основные свойства, содержащие ответ сервера:

status
HTTP-код ответа: 200, 404, 403. Может быть также равен 0, если сервер не ответил или при запросе на другой домен.
statusText
Текстовое описание статуса от сервера: OK, Not Found, Forbidden и так далее.
responseText
Текст ответа сервера.

Есть и ещё одно свойство, которое используется гораздо реже:

responseXML
Если сервер вернул XML, снабдив его правильным заголовком Content-type: text/xml, то браузер создаст из него XML-документ. П

Это свойство  используется редко, так как обычно используют не XML, а JSON. То есть, сервер возвращает JSON в виде текста, который браузер превращает в объект вызовом JSON.parse(xhr.responseText).

Синхронные и асинхронные запросы

Если в методе open установить параметр async равным false, то запрос будет синхронным.

Синхронные вызовы используются чрезвычайно редко, так как блокируют взаимодействие со страницей до окончания загрузки. Посетитель не может даже прокручивать её. Никакой JavaScript не может быть выполнен, пока синхронный вызов не завершён – в общем, в точности те же ограничения как alert.

// Синхронный запрос xhr.open('GET', 'phones.json', false); 
// Отсылаем его xhr.send(); // ...весь JavaScript "подвиснет", пока запрос не завершится

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

Из-за такой вот блокировки получается, что нельзя отослать 2 запроса одновременно. Кроме того, забегая вперёд, заметим, что ряд продвинутых возможностей, таких как возможность делать запросы на другой домен и указывать таймаут, в синхронном режиме не работают.

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

Для того, чтобы запрос стал асинхронным, укажем параметр async равным true.

Изменённый JS-код:

var xhr1 = new XMLHttpRequest(); 
xhr1.open('GET', 'phones.json', true); 
xhr1.send(); 
// (1) xhr1.onreadystatechange = function() { 
// (3) if (xhr1.readyState != 4) return; 
button.innerHTML = 'Готово!'; 
if (xhr1.status != 200) { 
alert(xhr1.status + ': ' + xhr1.statusText); 
} else { alert(xhr1.responseText); }
 } button.innerHTML = 'Загружаю...'; 
// (2) button.disabled = true;

Если в open указан третий аргумент true (или если третьего аргумента нет), то запрос соответственно выполняется асинхронно. Это означает, что после вызова xhr.send() в строке (1) код не «зависает», а преспокойно продолжает выполняться, выполняется строка (2), а результат приходит через событие (3).

Событие readystatechange

Событие readystatechange происходит несколько раз в процессе отсылки и получения ответа от сервера. При этом вы можете посмотреть «текущее состояние запроса» в свойстве xhr.readyState.

В примере выше мы использовали только состояние 4 (запрос завершён), но есть и другие.

Все состояния, по спецификации:

const unsigned short UNSENT = 0; 
// начальное состояние const unsigned short OPENED = 1; 
// вызван open const unsigned short HEADERS_RECEIVED = 2; 
// получены заголовки const unsigned short LOADING = 3; 
// загружается тело (получен очередной пакет данных) const unsigned short DONE = 4; 
// запрос завершён

Запрос проходит их в порядке 0 → 1 → 2 → 3 → … → 3 → 4, состояние 3 повторяется при каждом получении очередного пакета данных по сети.

Точка разрыва пакетов не гарантирована

При состоянии readyState=3 (получен очередной пакет) мы можем посмотреть текущие данные в responseText и, казалось бы, могли бы работать с этими данными как с «ответом на текущий момент».

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

Чем это опасно? Хотя бы тем, что символы русского языка в кодировке UTF-8 кодируются двумя байтами каждый – и разрыв может возникнуть между ними.

Получится, что при очередном readyState в конце responseText будет байт-полсимвола, то есть он не будет корректной строкой – частью ответа! Если в скрипте как-то по-особому это не обработать, то неизбежны проблемы.

HTTP-заголовки

XMLHttpRequest умеет как указывать свои заголовки в запросе, так и читать присланные в ответ.

Для работы с HTTP-заголовками есть 3 метода:

setRequestHeader(name, value)
Устанавливает заголовок name запроса со значением value соотвественно.

Например:

xhr.setRequestHeader('Content-Type', 'application/json');

Ограничения на заголовки

Нельзя установить заголовки, которые контролирует браузер, например Referer или Host.

Это ограничение используется в целях безопасности и для контроля корректности запроса.

Поставленный заголовок нельзя снять

Особенностью XMLHttpRequest является то, что отменить setRequestHeader невозможно.

Повторные вызовы лишь добавляют информацию к заголовку, например:

xhr.setRequestHeader('X-Auth', '123'); 
xhr.setRequestHeader('X-Auth', '456'); // в результате будет заголовок: // X-Auth: 123, 456

getResponseHeader(name)

Вернет значение заголовка ответа name, кроме Set-Cookie и Set-Cookie2.

Например:

xhr.getResponseHeader('Content-Type')

getAllResponseHeaders()

Вернет все заголовки ответа, кроме Set-Cookie и Set-Cookie2.

Заголовки возвращаются в виде единой строки, например:

Cache-Control: max-age=31536000
Content-Length: 4260
Content-Type: image/png
Date: Sat, 08 Sep 2012 16:53:16 GMT
Между заголовками стоит перевод строки в два символа «rn» (не зависит от ОС), значение заголовка отделено двоеточием с пробелом «: «.  Этот формат задан стандартом.

Таким образом, если хочется получить объект с парами заголовок-значение, то эту строку необходимо разбить и обработать.

Таймаут

Максимальную продолжительность асинхронного запроса можно задать таким свойством timeout:

xhr.timeout = 30000; // 30 секунд (в миллисекундах)

При превышении этого времени запрос будет оборван и сгенерировано событие ontimeout:

xhr.ontimeout = function() { alert( 'Извините, запрос превысил максимальное время' ); }

Все события

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

  • loadstart – запрос начат.
  • progress – браузер получил очередной пакет данных и можно прочитать текущие полученные данные в responseText.
  • abort – запрос был отменён.
  • error – случилась ошибка.
  • load – запрос был успешно  завершён.
  • timeout – запрос был прекращён по таймауту.
  • loadend – запрос был завершён

Используя эти события можно более удобно отслеживать загрузку (onload) и ошибку (onerror), а также количество загруженных данных (onprogress).

Итоги

Итак  вот типовой код для GET-запроса при помощи  объекта XMLHttpRequest:

var xhr = new XMLHttpRequest(); 
xhr.open('GET', '/my/server', true); 
xhr.send(); xhr.onreadystatechange = function() { 
if (this.readyState != 4) return; 
// по окончании запроса доступны: 
// status, statusText // responseText, 
responseXML (при content-type: text/xml) if (this.status != 200) { 
// обработать ошибку alert( 'ошибка: ' + (this.status ? this.statusText : 'запрос не удался') ); 
return; } // получить результат из this.responseText или this.responseXML }

Мы рассмотрели такие  методы XMLHttpRequest:

  • open(method, url, async, user, password)
  • send(body)
  • abort()
  • setRequestHeader(name, value)
  • getResponseHeader(name)
  • getAllResponseHeaders()

Свойства XMLHttpRequest:

  • timeout
  • responseText
  • responseXML
  • status
  • statusText

События:

  • onreadystatechange
  • ontimeout
  • onerror
  • onload
  • onprogress
  • onabort
  • onloadstart
  • onloadend

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Поделиться

Твитнуть

Поделиться

Также читайте

(Visited 452 times, 1 visits today)

XMLHttpRequest:событие ошибки

error событие вызывается , когда запрос произошла ошибка.

Syntax

Используйте имя события в таких методах, как addEventListener() , или установите свойство обработчика событий.

addEventListener('error', event => { })

onerror = event => { }

Event type

Event properties

В дополнение к свойствам, перечисленным ниже, доступны свойства из родительского интерфейса Event .

lengthComputableRead only

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

loadedRead only

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

totalRead only

64-разрядное целое число без знака, представляющее общий объем работы, выполняемой базовым процессом. При загрузке ресурса с использованием HTTP это Content-Length (размер тела сообщения) и не включает заголовки и другие накладные расходы.

Examples

Live example

HTML

<div class="controls">
    <input class="xhr success" type="button" name="xhr" value="Click to start XHR (success)" />
    <input class="xhr error" type="button" name="xhr" value="Click to start XHR (error)" />
    <input class="xhr abort" type="button" name="xhr" value="Click to start XHR (abort)" />
</div>

<textarea readonly class="event-log"></textarea>

JS

const xhrButtonSuccess = document.querySelector('.xhr.success');
const xhrButtonError = document.querySelector('.xhr.error');
const xhrButtonAbort = document.querySelector('.xhr.abort');
const log = document.querySelector('.event-log');

function handleEvent(e) {
    log.textContent = log.textContent + `${e.type}: ${e.loaded} bytes transferredn`;
}

function addListeners(xhr) {
    xhr.addEventListener('loadstart', handleEvent);
    xhr.addEventListener('load', handleEvent);
    xhr.addEventListener('loadend', handleEvent);
    xhr.addEventListener('progress', handleEvent);
    xhr.addEventListener('error', handleEvent);
    xhr.addEventListener('abort', handleEvent);
}

function runXHR(url) {
    log.textContent = '';

    const xhr = new XMLHttpRequest();
    addListeners(xhr);
    xhr.open("GET", url);
    xhr.send();
    return xhr;
}

xhrButtonSuccess.addEventListener('click', () => {
    runXHR('dgszyjnxcaipwzy.jpg');
});

xhrButtonError.addEventListener('click', () => {
    runXHR('https://somewhere.org/i-dont-exist');
});

xhrButtonAbort.addEventListener('click', () => {
    runXHR('dgszyjnxcaipwzy.jpg').abort();
});

Result

Specifications

Browser compatibility

Desktop Mobile
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox для Android Opera Android Safari на IOS Samsung Internet
error_event

1

12

1

10

≤12.1

1.3

≤37

18

4

≤12.1

1

1.0

See also

  • Связанные события: loadstart , load , progress , loadend , abort
  • Monitoring progress


Web APIs

  • XMLHttpRequest:прерывание события

    Событие abort срабатывает,когда запрос был прерван,например,потому что программа вызвала XMLHttpRequest.abort().

  • XMLHttpRequest.channel

    XMLHttpRequest.channel-это nsIChannel,который используется объектом при выполнении Последнее изменение:Апр 11,2022,автором MDN contributors

  • XMLHttpRequest.getAllResponseHeaders()

    Метод XMLHttpRequest getAllResponseHeaders()возвращает разделенные CRLF заголовки в виде строки или null,если ни один из них не был получен.

  • XMLHttpRequest.getResponseHeader()

    Метод XMLHttpRequest getResponseHeader() возвращает строку, содержащую текст определенного значения заголовка.

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Xhr response code 504 xhr responsetext undefined jquery status error
  • Xhr post error
  • Xhr poll error socket io
  • Xhr failed visual studio code ошибка как исправить
  • Xhr error вконтакте

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии