Xml httprequest: описание, применение, частые проблемы

«Непростые» запросы

В кросс-доменном можно указать не только , но и любой другой метод, например , .

Когда-то никто и не думал, что страница сможет сделать такие запросы. Поэтому ряд веб-сервисов написаны в предположении, что «если метод – нестандартный, то это не браузер». Некоторые веб-сервисы даже учитывают это при проверке прав доступа.

Чтобы пресечь любые недопонимания, браузер использует предзапрос в случаях, когда:

  • Если метод – не GET / POST / HEAD.
  • Если заголовок имеет значение отличное от , или , например .
  • Если устанавливаются другие HTTP-заголовки, кроме , , .

…Любое из условий выше ведёт к тому, что браузер сделает два HTTP-запроса.

Первый запрос называется «предзапрос» (английский термин «preflight»). Браузер делает его целиком по своей инициативе, из JavaScript мы о нём ничего не знаем, хотя можем увидеть в инструментах разработчика.

Этот запрос использует метод . Он не содержит тела и содержит название желаемого метода в заголовке , а если добавлены особые заголовки, то и их тоже – в .

Его задача – спросить сервер, разрешает ли он использовать выбранный метод и заголовки.

На этот запрос сервер должен ответить статусом 200, без тела ответа, указав заголовки и, при необходимости, .

Дополнительно он может указать , где – количество секунд, на которые нужно закэшировать разрешение. Тогда при последующих вызовах метода браузер уже не будет делать предзапрос.

Давайте рассмотрим предзапрос на конкретном примере.

Рассмотрим запрос , который используется в протоколе WebDAV для управления файлами через HTTP:

Этот запрос «непростой» по двум причинам (достаточно было бы одной из них):

  1. Метод .
  2. Заголовок .

Поэтому браузер, по своей инициативе, шлёт предварительный запрос :

Обратим внимание на детали:

  • Адрес – тот же, что и у основного запроса: .
  • Стандартные заголовки запроса , , присутствуют.
  • Кросс-доменные специальные заголовки запроса:
    • – домен, с которого сделан запрос.
    • – желаемый метод.
    • – желаемый «непростой» заголовок.

На этот запрос сервер должен ответить статусом 200, указав заголовки и .

Но в протоколе WebDav разрешены многие методы и заголовки, которые имеет смысл сразу перечислить в ответе:

Ответ должен быть без тела, то есть только заголовки.

Браузер видит, что метод – в числе разрешённых и заголовок – тоже, и дальше он шлёт уже основной запрос.

При этом ответ на предзапрос он закэширует на 86400 сек (сутки), так что последующие аналогичные вызовы сразу отправят основной запрос, без .

Основной запрос браузер выполняет уже в «обычном» кросс-доменном режиме:

Ответ сервера, согласно спецификации , может быть примерно таким:

Так как содержит правильный домен, то браузер вызовет и запрос будет завершён.

サマリ

を使用した GET リクエストの典型的なコード:

実施にはより多くのイベントがあり、 でリストされています(ライフサイクル順):

  • – リクエストが開始された
  • – レスポンスのデータパケットが到着し、その時点のレスポンス本文全体は にあります
  • – リクエストが 呼び出しによりキャンセルされた
  • – 接続エラーが発生。e.g. 間違ったドメイン名など. 404 などのHTTPエラーでは発生しません。
  • – リクエストが正常に終了した
  • – タイムアウトでリクエストがキャンセルされた(タイムアウトが設定された場合のみ)).
  • – , , or の後に発生します。.

, , , と イベントは相互に排他的です。それらの1つだけが発生します。

最も使われているイベントはロード完了 (), ロード失敗()です。あるいは、単一の ハンドラを使用して何が起こったのかを確認するためにレスポンスをチェックします。

すでに別のイベント を見てきました。歴史的には、仕様が定まるずっと前からありました。最近では、これを使う必要はありません。新しいイベントに置き換えることができますが、多くの場合、古いスクリプトにあります。

特にアップロードを追跡する必要がある場合は、 オブジェクトで同じイベントをリッスンする必要があります。

See also

  • MDN articles about XMLHttpRequest:
    • AJAX — Getting Started
    • Using XMLHttpRequest
    • HTML in XMLHttpRequest
  • XMLHttpRequest references from W3C and browser vendors:
    • W3C: XMLHttpRequest (base features)
    • W3C: XMLHttpRequest (latest editor’s draft with extensions to the base functionality, formerly XMLHttpRequest Level 2
    • Microsoft documentation
  • «Using the XMLHttpRequest Object» (jibbering.com)
  • XMLHttpRequest — REST and the Rich User Experience
  • HTML5 Rocks — New Tricks in XMLHttpRequest2
  • Thread on the naming convention of
  • — how to access from JSM modules etc which do not have access to DOM

    • Components.utils.importGlobalProperties
    • nsIXMLHttpRequest

Извлечение данных

Загрузка файла в виде двоичного объекта с помощью XHR всегда была проблемой. С технической точки зрения это было даже невозможно. Один из известных способов заключается в переопределении mime-типа пользовательской кодировкой, как показано ниже.

Ранее содержимое картинки можно было извлечь таким способом:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);

// Hack to pass bytes through unprocessed.
xhr.overrideMimeType('text/plain; charset=utf-8');

xhr.onreadystatechange = function(e) {
  if (this.readyState == 4 && this.status == 200) {
    var binStr = this.responseText;
    for (var i = 0, len = binStr.length; i < len; ++i) {
      var c = binStr.charCodeAt(i);
      //String.fromCharCode(c & 0xff);
      var byte = c & 0xff;  // byte at offset i
    }
  }
};

xhr.send();

Этот способ работает, однако элемент вовсе не является большим двоичным объектом (элементом blob). Это двоичная строка, представляющая файл картинки. Мы заставляем сервер вернуть данные в необработанном виде. Хотя этот прием работает, я не рекомендую использовать его. При попытке принудительно перевести данные в нужный формат с помощью манипуляций с кодировкой и строками всегда возникают проблемы.

Указание формата ответа

В предыдущем примере картинка загружалась в виде двоичного файла путем переопределения mime-типа сервера и обработки текста как двоичной строки. Вместо этого воспользуемся новыми возможностями технологии : свойствами и , позволяющими указать браузеру желаемый формат ответа.

xhr.responseType

Прежде чем отправить запрос, необходимо задать для свойства значение text, arraybuffer, blob или document

Обратите внимание: если установить значение или опустить его, по умолчанию выбирается формат text.
xhr.response

После успешного выполнения запроса свойство response будет содержать запрошенные данные в формате , , или в соответствии со значением .. Переработаем предыдущий пример с использованием этой новой возможности

Теперь мы извлекаем данные картинки в формате вместо строки. Передаем буфер в API и получаем объект

Переработаем предыдущий пример с использованием этой новой возможности. Теперь мы извлекаем данные картинки в формате вместо строки. Передаем буфер в API и получаем объект .

BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
  if (this.status == 200) {
    var bb = new BlobBuilder();
    bb.append(this.response); // Note: not xhr.responseText

    var blob = bb.getBlob('image/png');
    ...
  }
};

xhr.send();

Так намного лучше.

Ответы в формате ArrayBuffer

 – это стандартный контейнер фиксированной длины для двоичных данных. Это очень удобный универсальный буфер для необработанной информации, но его главное достоинство – возможность создавать «представления» исходных данных с помощью типизированных массивов JavaScript. Фактически на базе одного источника можно сформировать несколько представлений. Например, можно создать 8-битный целочисленный массив, который использует тот же объект , что и 32-битный массив на базе тех же данных. Исходная информация остается неизменной: она просто представляется в разном виде.

В примере ниже мы извлекаем ту же картинку в формате , но на этот раз создаем из данных в буфере 8-битный целочисленный массив.

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
  var uInt8Array = new Uint8Array(this.response); // this.response == uInt8Array.buffer
  // var byte3 = uInt8Array; // byte at offset 4
  ...
};

xhr.send();

Ответы в формате Blob

Для непосредственной работы с объектами без операций с отдельными байтами файла можно использовать значение .

window.URL = window.URL || window.webkitURL;  // Take care of vendor prefixes.

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';

xhr.onload = function(e) {
  if (this.status == 200) {
    var blob = this.response;

    var img = document.createElement('img');
    img.onload = function(e) {
      window.URL.revokeObjectURL(img.src); // Clean up after yourself.
    };
    img.src = window.URL.createObjectURL(blob);
    document.body.appendChild(img);
    ...
  }
};

xhr.send();

Объект можно использовать по разному: например, сохранить его в индексированной базе данных, записать в файловую систему HTML5 или , как показано в этом примере.

Использование XMLHTTPRequest

Различают два использования XmlHttpRequest. Первое — самое простое, синхронное.

Синхронный XMLHttpRequest

В этом примере через XMLHTTPRequest с сервера запрашивается страница http://example.org/, и текст ответа сервера показывается через alert().

var xmlhttp = getXmlHttp()
xmlhttp.open('GET', '/xhr/test.html', false);
xmlhttp.send(null);
if(xmlhttp.status == 200) {
  alert(xmlhttp.responseText);
}

Здесь сначала создается запрос, задается открытие () синхронного соединение с адресом /xhr/test.html и запрос отсылается с null,
т.е без данных.

При синхронном запросе браузер «подвисает» и ждет на строчке 3, пока сервер не ответит на запрос. Когда ответ получен — выполняется строка 4, код ответа сравнивается с 200 (ОК), и при помощи alert
печатается текст ответа сервера. Все максимально просто.

Свойство responseText получит такой же текст страницы, как браузер, если бы Вы в перешли на /xhr/test.html. Для сервера
GET-запрос через XmlHttpRequest ничем не отличается от обычного перехода на страницу.

Асинхронный XMLHttpRequest

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

var xmlhttp = getXmlHttp()
xmlhttp.open('GET', '/xhr/test.html', true);
xmlhttp.onreadystatechange = function() {
  if (xmlhttp.readyState == 4) {
     if(xmlhttp.status == 200) {
       alert(xmlhttp.responseText);
         }
  }
};
xmlhttp.send(null);

Асинхронность включается третьим параметром функции open. В отличие от синхронного запроса, функция send() не останавливает
выполнение скрипта, а просто отправляет запрос.

Запрос xmlhttp регулярно отчитывается о своем состоянии через вызов функции xmlhttp.onreadystatechange. Состояние под номером 4 означает конец выполнения, поэтому функция-обработчик
при каждом вызове проверяет — не настало ли это состояние.

Вообще, список состояний readyState такой:

  • 0 — Unitialized
  • 1 —
  • 2 — Loaded
  • 3 — Interactive
  • 4 — Complete

Состояния 0-2 вообще не используются.

Вызов функции с состоянием Interactive в теории должен происходить каждый раз при получении очередной порции данных от сервера.
Это могло бы быть удобным для обработки ответа по частям, но Internet Explorer не дает доступа к уже полученной части ответа.

Firefox дает такой доступ, но для обработки запроса по частям состояние Interactive все равно неудобно из-за сложностей обнаружения ошибок соединения.
Поэтому Interactive тоже не используется.

На практике используется только последнее, Complete.

Если хотите углубиться в тонкости багов браузеров c readyState, отличными от 4, то многие из них рассмотрены в статье на.

Не используйте синхронные запросы

Синхронные запросы применяются только в крайнем случае, когда кровь из носу необходимо дождаться ответа сервера до продолжения скрипта. В 999 случаях из 1000
можно использовать асинхронные запросы. При этом общий алгоритм такой:

  1. Делаем асинхронный запрос
  2. Рисуем анимированную картинку или просто запись типа «Loading…»
  3. В onreadystatechange при достижении состояния 4 убираем Loading и, в зависимости от status вызываем обработку ответа или ошибки.

Кроме того, иногда полезно ставить ограничение на время запроса. Например, хочется генерировать ошибку, если запрос висит более 10 секунд.

Для этого сразу после send() через setTimeout ставится вызов обработчика ошибки, который очищается при получении ответа и обрывает запрос с генерацией ошибки,
если истекли 10 секунд.

Таймаут на синхронный запрос ставить нельзя, браузер может висеть долго-долго.. А вот на асинхронный — пожалуйста.

Этот пример демонстрирует такой таймаут.

var xmlhttp = getXmlHttp()
xmlhttp.open("POST", "/someurl", true);
xmlhttp.onreadystatechange=function(){
  if (xmlhttp.readyState != 4) return
  clearTimeout(timeout) // очистить таймаут при наступлении readyState 4
  if (xmlhttp.status == 200) {
      // Все ок
      ...
      alert(xmlhttp.responseText);
      ...
  } else {
      handleError(xmlhttp.statusText) // вызвать обработчик ошибки с текстом ответа
  }
}
xmlhttp.send("a=5&b=4");
// Таймаут 10 секунд
var timeout = setTimeout( function(){ xmlhttp.abort(); handleError("Time over") }, 10000);
function handleError(message) {
  // обработчик ошибки
  ...
  alert("Ошибка: "+message)
  ...
}

GET и POST-запросы. Кодировка.

Во время обычного submit’а формы браузер сам кодирует значения полей и составляет тело GET/POST-запроса для посылки на сервер. При работе через XmlHttpRequest, это нужно делать самим, в javascript-коде. Большинство проблем и вопросов здесь связано с непониманием, где и какое кодирование нужно осуществлять.

Вначале рассмотрим общее кодирование запросов, ниже — правильную работу с русским языком для windows-1251.

Существуют два вида кодирования HTTP-запроса. Основной — urlencoded, он же — стандартное кодирование URL. Пробел представляется как %20, русские буквы и большинство спецсимволов кодируются, английские буквы и дефис оставляются как есть.

Способ, которым следует кодировать данные формы при submit’е, задается в ее HTML-таге:

<form method="get"> // метод GET с кодировкой по умолчанию
<form method="post" enctype="application/x-www-form-urlencoded"> // enctype явно задает кодировку
<form method="post"> // метод POST с кодировкой по умолчанию (urlencoded, как и предыдущая форма)

Если форма submit’ится обычным образом, то браузер сам кодирует (urlencode) название и значение каждого поля данных ( и т.п.) и отсылает форму на сервер в закодированном виде.

Формируя XmlHttpRequest, мы должны формировать запрос «руками», кодируя поля функцией .

Конечно, пропускать через encodeURIComponent стоит только те переменные, в которых могут быть спецсимволы или не английские буквы, т.е которые и будут как раз закодированы.

Например, для посылки GET-запроса с произвольными параметрами name и surname, их необходимо закодировать вот так:

// Пример с GET
...
var params = 'name=' + encodeURIComponent(name) + '&surname=' + encodeURIComponent(surname)
xmlhttp.open("GET", '/script.html?'+params, true)
...
xmlhttp.send(null)

В методе POST параметры передаются не в URL, а в теле, посылаемом через . Поэтому нужно указывать не в адресе, а при вызове

Кроме того, при POST обязателен заголовок Content-Type, содержащий кодировку. Это указание для сервера — как обрабатывать (раскодировать) пришедший запрос.

// Пример с POST
...
var params = 'name=' + encodeURIComponent(name) + '&surname=' + encodeURIComponent(surname)
xmlhttp.open("POST", '/script.html', true)
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
...
xmlhttp.send(params)

Заголовки Content-Length, Connection в POST-запросах, хотя их и содержат некоторые «руководства», обычно не нужны. Используйте их, только если Вы действительно знаете, что делаете.

Запросы multipart/form-data

Второй способ кодирования — это отсутствие кодирования. Например, кодировать не нужно для пересылки файлов. Он указывается в форме (только для POST) так:

<form method="post" enctype="multipart/form-data">

В этом случае при отправке данных на сервер ничего не кодируется. А сервер, со своей стороны, посмотрев на Content-Type(=multipart/form-data), поймет, что пришло.

Возможности XmlHttpRequest позволяют создать запрос с любым телом. Например, можно вручную сделать POST-запрос, загружающий на сервер файл. Функционал создания
таких запросов есть, в частности, во фреймворке . Но можно реализовать его и самому, прочитав о нужном формате тела POST и заголовках.

Кодировка (языковая)

Если Вы используете только UTF-8 — пропустите эту секцию.

Все идущие на сервер параметры GET/POST, кроме случая multipart/form-data, кодируются в UTF-8. Не в кодировке страницы, а именно в UTF-8. Поэтому, например, в PHP их нужно при необходимости перекодировать функцией iconv.

<?php
// ajax.php
$name = iconv('UTF8','CP1251',$_GET);
?>

С другой стороны, ответ с сервера браузер воспринимает именно в той кодировке, которая указана в заголовке ответа Content-Type. Т.е, опять же, в PHP, чтобы браузер воспринял ответ в windows-1251 и нормально отобразил данные на странице в windows-1251,
нужно послать заголовок с кодировкой в php-коде, например так:

<?php
// ajax.php
header('Content-Type: text/plain; charset=utf-8');
?>

Или же, такой заголовок должен добавить сервер. Например, в apache автоматически добавляется кодировка опцией:

# в конфиге апача
AddDefaultCharset windows-1251

POST с urlencoded

В методе POST параметры передаются не в URL, а в теле запроса. Оно указывается в вызове .

В стандартных HTTP-формах для метода POST доступны , задаваемые через атрибут :

В зависимости от браузер кодирует данные соответствующим способом перед отправкой на сервер.

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

В частности, при POST обязателен заголовок , содержащий кодировку. Это указание для сервера – как обрабатывать (раскодировать) пришедший запрос.

Для примера отправим запрос в кодировке :

Только UTF-8

Всегда используется только кодировка UTF-8, независимо от языка и кодировки страницы.

Если сервер вдруг ожидает данные в другой кодировке, к примеру windows-1251, то их нужно будет перекодировать.

Introduction

XMLHttpRequest (XHR) is a browser-level API that enables the client to
script data transfers via JavaScript. XHR made its first debut in
Internet Explorer 5, became one of the key technologies behind the
Asynchronous JavaScript and XML (AJAX) revolution, and is now a
fundamental building block of nearly every modern web application.

Prior to XHR, the web page had to be refreshed to send or fetch any
state updates between the client and server. With XHR, this workflow
could be done asynchronously and under full control of the application
JavaScript code. XHR is what enabled us to make the leap from building
pages to building interactive web applications in the browser.

However, the power of XHR is not only that it enabled asynchronous
communication within the browser, but also that it made it simple. XHR is
an application API provided by the browser, which is to say that the
browser automatically takes care of all the low-level connection
management, protocol negotiation, formatting of HTTP requests, and much
more:

  • The browser manages connection establishment, pooling, and
    termination.

  • The browser determines the best HTTP(S) transport (HTTP/1.0, 1.1,
    2).

  • The browser handles HTTP caching, redirects, and content-type
    negotiation.

  • The browser enforces security, authentication, and privacy
    constraints.

  • And more…

Free from worrying about all the low-level details, our applications
can focus on the business logic of initiating requests, managing their
progress, and processing returned data from the server. The combination
of a simple API and its ubiquitous availability across all the browsers
makes XHR the «Swiss Army knife» of networking in the browser.

As a result, nearly every networking use case (scripted downloads,
uploads, streaming, and even real-time notifications) can and have been
built on top of XHR. Of course, this doesn’t mean that XHR is the most
efficient transport in each case—in fact, as we will see, far from it—but
it is nonetheless often used as a fallback transport for older clients,
which may not have access to newer browser networking APIs. With that in
mind, let’s take a closer look at the latest capabilities of XHR, its use
cases, and performance do’s and don’ts.

Upload progress

The event triggers only on the downloading stage.

That is: if we something, 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 doesn’t help here.

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

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

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

Example of handlers:

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

Пишем PHP-скрипт для сервера

Задача скрипта пока будет очень простой — ему нужно будет получить наши данные и показать, что всё пришло как нужно. В PHP уже встроена команда, которая разбирает JSON-строку на составляющие, поэтому весь скрипт будет занимать три строчки:

Для получения данных наш PHP-скрипт использует стандартную команду . Она просто ждёт, когда что-то прилетит, и отправляет результат в выбранную переменную. Дальше её можно разобрать как JSON-объект командой и работать с данными напрямую.

Последняя команда отправляет в ответ то, что написано в двойных кавычках. Там мы обращаемся к переменной $data, где хранятся присланные данные. Именно этот ответ будет обрабатывать скрипт на JavaScript, в функции xhr.onreadystatechange, которую мы прописали раньше.

Сам код нам нужно сохранить как json.php и положить в папку /projects/json/ на нашем сайте — так мы прописали в скрипте на JavaScript.

Основы

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

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

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

  1. Создать .

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

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

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

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

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

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

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

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

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

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

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

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

Код состояния HTTP (число): , , и так далее, может быть в случае, если ошибка не связана с HTTP.
Сообщение о состоянии ответа HTTP (строка): обычно для , для , для , и так далее.
(в старом коде может встречаться как )
Тело ответа сервера.

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

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

URL с параметрами

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

Изменения состояния XHR

Если метод был вызван успешно, свойству объекта XMLHttpRequest будет присвоено значение 1 (Open). После того как заголовки HTTP-ответа были получены, для свойства readyState объекта XHR назначается значение 2 (HEADERS_RECEIVED). После загрузки содержимого ответа HTTP свойству readyState объекта XHR должно быть присвоено значение 3 (Loading).

После завершения загрузки HTTP-ответа для свойства readyState объекта XHR должно быть назначено значение 4 (Done). Слушатель будет реагировать только на изменения состояния, которые возникают после его определения. Чтобы обнаружить состояния 1 и 2, слушатель должен быть определен до вызова open. Открытый метод должен быть применен до вызова send.

Этот метод прерывает запрос, если объект readyState объекта XHR еще не стал 4 (Done). Метод abort гарантирует, что обработчик обратного вызова не будет вызван во время асинхронного запроса. Некоторые библиотеки AJAX используют прерывание, чтобы отменить потенциальный дубликат или испорченные запросы.

responseType, response, responseText, атрибуты responseXML

Вы можете установить responseType перед отправкой запроса, чтобы указать тип возвращаемых данных ответа.Существуют следующие типы responseType:

Типы описание
empty string Пустая строка, это значение по умолчанию
arraybuffer Двоичный буферный массив
blob Бинарный большой объект
document Тип документа
json Тип JSON
text Тип текста

При обработке данных ответа вам необходимо определить тип возвращаемых данных на основе responseType. Когда responseType является текстом или пустой строкой, вы можете использовать свойство responseText. Когда responseType является другим типом, при вызове responseText будет возникать исключение; когда responseType является документом или пустым responseXML, исключение произойдет при вызове responseXML, когда responseType — это другие типы.Когда responseType не является пустой строкой, text Когда используется тип документа, его необходимо преобразовать в определенный тип для анализа.

Кодировка multipart/form-data

Кодировка urlencoded за счёт замены символов на может сильно «раздуть» общий объём пересылаемых данных. Поэтому для пересылки файлов используется другая кодировка: multipart/form-data.

В этой кодировке поля пересылаются одно за другим, через строку-разделитель.

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

Форма при такой кодировке будет выглядеть примерно так:

…То есть, поля передаются одно за другим, значения не кодируются, а чтобы было чётко понятно, какое значение где – поля разделены случайно сгенерированной строкой, которую называют «boundary» (англ. граница), в примере выше это :

Сервер видит заголовок , читает из него границу и раскодирует поля формы.

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

Однако, никто не мешает использовать эту кодировку всегда для POST запросов. Для GET доступна только urlencoded.

JS Tutorial

JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS LetJS ConstJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS EventsJS StringsJS String MethodsJS String SearchJS String TemplatesJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS Array ConstJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop For InJS Loop For OfJS Loop WhileJS BreakJS IterablesJS SetsJS MapsJS TypeofJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS Arrow FunctionJS ClassesJS JSONJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved Words

Monitoring progress

provides the ability to listen to various events that can occur while the request is being processed. This includes periodic progress notifications, error notifications, and so forth.

Firefox 3.5 adds support for DOM progress event monitoring of transfers; this follows the Web API specification for progress events.

var req = new XMLHttpRequest();

req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.addEventListener("error", transferFailed, false);
req.addEventListener("abort", transferCanceled, false);

req.open();

...

// progress on transfers from the server to the client (downloads)
function updateProgress(evt) {
  if (evt.lengthComputable) {
    var percentComplete = evt.loaded / evt.total;
    ...
  } else {
    // Unable to compute progress information since the total size is unknown
  }
}

function transferComplete(evt) {
  alert("The transfer is complete.");
}

function transferFailed(evt) {
  alert("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  alert("The transfer has been canceled by the user.");
}

Lines 3-6 add event listeners for the various events that are sent while performing a data transfer using .  See and for details on these events.

Note: You need to add the event listeners before calling on the request.  Otherwise the progress events will not fire.

The progress event handler, specified by the function in this example, receives the total number of bytes to transfer as well as the number of bytes transferred so far in the event’s and fields.  However, if the field is false, the total length is not known and will be zero.

Progress events exist for both download and upload transfers. The download events are fired on the object itself, as shown in the above sample. The upload events are fired on the object, as shown below:

var req = new XMLHttpRequest();

req.upload.addEventListener("progress", updateProgress, false);
req.upload.addEventListener("load", transferComplete, false);
req.upload.addEventListener("error", transferFailed, false);
req.upload.addEventListener("abort", transferCanceled, false);

req.open();

Note: Progress events are not available for the  protocol.

If, for example, you wish to provide progress information to the user while the document is being received, you can use code like this:

function onProgress(e) {
  var percentComplete = (e.position / e.totalSize)*100;
  // ...
}

function onError(e) {
  alert("Error " + e.target.status + " occurred while receiving the document.");
}

function onLoad(e) {
  // ...
}
// ...
var req = new XMLHttpRequest();
req.onprogress = onProgress; // or req.addEventListener("progress", onProgress, false);
req.open("GET", url, true);
req.onload = onLoad; // or req.addEventListener("load", onLoad, false);
req.onerror = onError; // or req.addEventListener("error", onError, false);
req.send(null);

The event’s attributes, and , indicate the current number of bytes received and the total number of bytes expected, respectively.

All of these events have their attribute set to the they correspond to.

Note: Firefox 3 properly ensures that the values of the , , and fields of the event object are set to reference the correct objects when calling event handlers for XML documents represented by . See error 198595 for details.

Свойства объекта XMLHttpRequest

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

responseText

Текст ответа сервера. Полный текст есть только при readyState=4, ряд браузеров дают доступ к полученной части ответа сервера при readyState=3.

responseXML

Ответ сервера в виде XML, при readyState=4.

Это свойство хранит объект типа XML document, с которым можно обращаться так же, как с обычным document. Например,

var authorElem = xmlhttp.responseXML.getElementById('author');

Чтобы браузер распарсил ответ сервера в свойство responseXML, в ответе должен быть заголовок Content-Type: text/xml.

Иначе свойство responseXML будет равно null.

status

Для HTTP-запросов — статусный код ответа сервера: 200 — OK, 404 — Not Found, и т.п. Браузер Internet Explorer может также присвоить status код ошибки WinInet,
например 12029 для ошибки «cannot connect».

Запросы по протоколам FTP, FILE:// не возвращают статуса, поэтому нормальным для них является status=0.

§Monitoring Download and Upload Progress

Network connectivity can be intermittent, and latency and bandwidth
are highly variable. So how do we know if an XHR request has succeeded,
timed out, or failed? The XHR object provides a convenient API for
listening to progress events (), which indicate the current status
of the request.

Event type Description

Times fired

loadstart Transfer has begun

once

progress Transfer is in progress

zero or more

error Transfer has failed

zero or once

abort Transfer is terminated

zero or once

load Transfer is successful

zero or once

loadend Transfer has finished once

Table 15-1. XHR progress events

Each XHR transfer begins with a loadstart and finishes with a
loadend event, and in between, one or more additional events are
fired to indicate the status of the transfer. Hence, to monitor progress
the application can register a set of JavaScript event listeners on the
XHR object:

var xhr = new XMLHttpRequest();
xhr.open('GET','/resource');
xhr.timeout = 5000; 

xhr.addEventListener('load', function() { ... }); 
xhr.addEventListener('error', function() { ... }); 

var onProgressHandler = function(event) {
  if(event.lengthComputable) {
    var progress = (event.loaded / event.total) * 100; 
    ...
  }
}

xhr.upload.addEventListener('progress', onProgressHandler); 
xhr.addEventListener('progress', onProgressHandler); 
xhr.send();
  1. Set request timeout to 5,000 ms (default: no timeout)

  2. Register callback for successful request

  3. Register callback for failed request

  4. Compute transfer progress

  5. Register callback for upload progress events

  6. Register callback for download progress events

Either the load or error event will fire once to
indicate the final status of the XHR transfer, whereas the
progress event can fire any number of times and provides a
convenient API for tracking transfer status: we can compare the
loaded attribute against total to estimate the amount
of transferred data.

Using FormData objects

The object lets you compile a set of key/value pairs to send using . It’s primarily intended for use in sending form data, but can be used independently from forms in order to transmit keyed data. The transmitted data is in the same format that the form’s  method would use to send the data if the form’s encoding type were set to «multipart/form-data».

You can build a object yourself, instantiating it then appending fields to it by calling its method, like this:

This example builds a object containing values for fields named «username» and «accountnum», then uses the method  to send the form’s data.

To construct a object that contains the data from an existing , specify that form element when creating the object:

newFormData = new FormData(someFormElement);

For example:

You can also add data to the object between retrieving it from a form and sending it, like this:

This lets you augment the form’s data before sending it along, to include additional information that’s not necessarily user editable on the form.

You can also send files using . Simply include an element of type «file»:

Then you can send it using code like the following:

Note that this example is directing the output to a Perl CGI script running on the server, and handles HTTP errors, although not prettily.

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

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

Adblock
detector