Xmlhttprequest

Заголовки ответа

По умолчанию при запросе к другому источнику JavaScript может получить доступ только к так называемым «простым» заголовкам ответа:

При доступе к любому другому заголовку ответа будет ошибка.

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

Пожалуйста, обратите внимание: в списке нет заголовка !

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

Чтобы разрешить JavaScript доступ к любому другому заголовку ответа, сервер должен указать заголовок . Он содержит список, через запятую, заголовков, которые не являются простыми, но доступ к которым разрешён.

Например:

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

Как работает HTTP, и зачем нам это знать

Программировать на PHP можно и без знания протокола HTTP, но есть ряд ситуаций, когда для решения задач нужно знать, как именно работает веб-сервер. Ведь PHP — это, в первую очередь, серверный язык программирования.

Протокол HTTP очень прост и состоит, по сути, из двух частей:

  • Заголовков запроса/ответа;
  • Тела запроса/ответа.

Сначала идёт список заголовков, затем пустая строка, а затем (если есть) тело запроса/ответа.

И клиент, и сервер могут посылать друг другу заголовки и тело ответа, но в случае с клиентом доступные заголовки будут одни, а с сервером — другие. Рассмотрим пошагово, как будет выглядеть работа по протоколу HTTP в случае, когда пользователь хочет загрузить главную страницу социальной сети «Вконтакте».

1. Браузер пользователя устанавливает соединение с сервером vk.com и отправляет следующий запрос:

GET / HTTP/1.1
Host: vk.com

2. Сервер принимает запрос и отправляет ответ:

3. Браузер принимает ответ и показывает готовую страницу

Больше всего нам интересен самый первый шаг, где браузер инициирует запрос к серверу vk.com
Рассмотрим подробнее, что там происходит. Первая строка запроса определяет несколько важных параметров, а именно:

  • Метод, которым будет запрошен контент;
  • Адрес страницы;
  • Версию протокола.

— это метод (глагол), который мы применяем для доступа к указанной странице. является самым часто используемым методом, потому что он говорит серверу о том, что клиент всего лишь хочет прочитать указанный документ. Но помимо есть и другие методы, один из них мы рассмотрим уже в следующем разделе.

После метода идет указание на адрес страницы — URI (универсальный идентификатор ресурса). В нашем случае мы запрашиваем главную страницу сайта, поэтому используется просто слэш — .
Последним в этой строке идет версия протокола и почти всегда это будет

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

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

Больше всего здесь интересен именно код состояния, он же код ответа сервера.
В этом примере код ответа — 200, что означает: сервер работает, документ найден и будет передан клиенту. Но не всегда всё идет гладко.
Например, запрошенный документ может отсутствовать или сервер будет перегружен, в таком случае клиент не получит контент, а код ответа будет отличным от 200.

  • 404 — если сервер доступен, но запрошённый документ не найден;
  • 503 — если сервер не может обрабатывать запросы по техническим причинам.

Спецификация HTTP 1.1 определяет 40 различных кодов HTTP.

После стартовой строки следуют заголовки, а затем тело ответа.

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

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

Выполнение асинхронных запросов с JavaScript и Ajax

В отличие от синхронного запроса, функция send() не останавливает выполнение скрипта, а просто отправляет запрос. Функция-обработчик onreadystatechange выполняет два действия:

  1. определяет состояний (readyState) запрашиваемого документа, ждет состояния 4 — Complete
  2. указывает функцию обратного вызова (callback). Функция обратного вызова позволяет серверу выполнить обратный вызов в коде вашей Web-страницы. Когда сервер завершает обработку запроса, он обращается к свойству onreadystatechange объект XMLHttpRequest. Затем активизирует любою функцию, указанную в этом свойстве.

Свойство onreadystatechange указывается перед вызовом send().

Пример:

function sendFrWhois(form) {
    var xhr = new XMLHttpRequest();
    var params = 'job=whois&indomain=i.ua';
    xhr.open('POST', 'http://wiki.dieg.info/', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) { // игнорируются все состояния, кроме Complete (завершен)
            if(xhr.status >= 200 && xhr.status < 300)
            // спецификация HTTP определяет кода в диапазоне 200-209 как успех, а
            // от 300 и выше различные ошибки
            {
                 alert(xhr.responseText);
            }
        }
    }
    xhr.send('job=whois&indomain=i.ua');   
}

Методы интерфейса

Метод Описание Chrome Firefox Opera Safari IExplorer Edge
abort() Позволяет прервать запрос, если он уже был отправлен. Да Да Да Да Да Да
getAllResponseHeaders() Возвращает все заголовки ответа, разделенные разрывом строки (CRLF) в виде строки или null, если ответ не был получен. Да Да Да Да Да Да
getResponseHeader() Возвращает строку, содержащую текст указанного заголовка, или null, если ответ еще не был получен, или заголовок не существует в ответе. Да Да Да Да Да Да
open() Позволяет инициализировать только что созданный запрос, или повторно инициализировать существующий запрос. Да Да Да Да Да Да
overrideMimeType() Позволяет переопределить MIME тип, возвращаемый сервером (задает тип MIME, отличный от того, который предоставляется сервером для использования при интерпретации данных, передаваемых в запросе). Да Да Да Да 11.0* Да
send() Позволяет отправить запрос на сервер. Да Да Да Да Да Да
setRequestHeader() Задает значение заголовка HTTP запроса. Да Да Да Да Да Да

Интерфейсы веб API

Использование 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)
  ...
}

Synchronous requests

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

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

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

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 , 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.

Как это устроено

Если мы хотим хра­нить дан­ные на сер­ве­ре и отправ­лять их туда в любой момент, нам нуж­но дей­ство­вать так:

  1. Собрать дан­ные в JSON-формат.
  2. Упа­ко­вать их в спе­ци­аль­ный запрос.
  3. Встро­ен­ны­ми сред­ства­ми JavaScript отпра­вить этот запрос на сер­вер по нуж­но­му адресу.
  4. Что­бы наш запрос был при­нят, по это­му адре­су на сер­ве­ре дол­жен нахо­дить­ся скрипт, кото­рый уме­ет рабо­тать с таки­ми запросами.
  5. А что­бы сер­вер в прин­ци­пе отве­чал на какие-то запро­сы, нам нуж­но его это­му обучить.

Пер­вые три пунк­та сде­ла­ем на кли­ен­те — нашей HTML-странице, а скрипт и настрой­ки — на сер­ве­ре. Скрипт будем писать на PHP, поэто­му, если не зна­е­те, что это и как с этим рабо­тать, — почи­тай­те.

Что­бы было про­ще, мы отпра­вим и обра­бо­та­ем на сер­ве­ре совсем малень­кий JSON — в нём все­го две пары «имя: зна­че­ние», но даже со слож­ным запро­сом всё будет рабо­тать так же.

GET-запрос

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

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

Прочие заголовки

Браузер автоматически добавит к запросу важнейшие HTTP-заголовки, такие как и .

По спецификации браузер запрещает их явную установку, как и некоторых других низкоуровневых HTTP-заголовков, которые могли бы ввести в заблуждение сервер относительно того, кто и сколько данных ему прислал, например . Это сделано в целях контроля правильности запроса и для безопасности.

Сообщаем про AJAX

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

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

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 NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS 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 Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS Arrow FunctionJS ClassesJS JSONJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved Words

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

<!DOCTYPE html>
<html>
	<head>
		<title>Использование JavaScript методов open() и send() объекта XMLHttpRequest</title>
	</head>
	<body>
		<button onclick = "getPhone()">Запросить телефон</button> <!-- добавляем атрибут событий onclick -->
		<div id = "demo"></div>
		<script>
	function getPhone() {
	  let xhr = new XMLHttpRequest(); // инициализируем переменную новым объектом XMLHttpRequest
	  xhr.open("GET", "user.json"); // определяем параметры для запроса 
	  xhr.send(); // отправляем запрос на сервер

	  xhr.onreadystatechange = function() {
	    // проверяем состояние запроса и числовой код состояния HTTP ответа
	    if (this.readyState == 4 && this.status == 200) {
	      const data = JSON.parse(this.responseText); // анализируем строку в формате JSON и инициализируем переменную значением, полученным в ходе анализа
	      document.getElementById("demo").innerHTML = "Телефон пользователя: " + data.phone; // находим элемент по id и изменяем его содержимое значением ключа объекта, содержащегося в переменной
	    }
	  }; 
	}
		</script>
	</body>
</html>

В этом примере с использованием атрибута событий onclick при нажатии на кнопку (HTML элемент <button>) вызываем функцию getPhone, которая:

Вызывает конструктор объекта XMLHttpRequest и инициализирует переменную новым объектом этого типа.
С помощью метода open() объекта XMLHttpRequest определяем параметры для запроса — указываем, что HTTP запрос будет осуществлен методом «GET», а в качестве URL адреса на который будет отправлен запрос мы задаем файл формата json

Обратите внимание, что файл размещен на том же сервере и в том же каталоге, что и документ с которого выполняется скрипт. Файл имеет следующий вид:

{
«firstName»: «Василий»,
«lastName»: «Джейсонов»,
«age»: 25,
«phone»: 88005553535
}

С помощью метода send() объекта XMLHttpRequest отправляем запрос на сервер.
С использованием обработчика событий onreadystatechange, вызываемого при запуске события readystatechange, то есть при каждом изменении свойства readyState объекта XMLHttpRequest мы вызываем функцию, которая проверяет состояние запроса, оно должно соответствовать значению 4 (операция полностью завершена) и числовой код состояния HTTP ответа (свойство status) должен соответствовать значению 200 (успешный запрос)

Если условия выполнены, то с использованием метода JSON.parse() анализируем строку в формате JSON и инициализируем переменную значением, полученным в ходе анализа. После этого с помощью метода getElementById() находим элемент с определенным глобальным атрибутом id и изменяем его содержимое значением ключа объекта, содержащегося в инициализированной ранее переменной.

Результат нашего примера:

Пример использования методов open() и send() объекта XMLHttpRequestJavaScript XMLHttpRequest

Простые запросы

  1. Простые.
  2. Все остальные.

Простые запросы будут попроще, поэтому давайте начнём с них.

– это запрос, удовлетворяющий следующим условиям:

  1. : GET, POST или HEAD
  2. – разрешены только:

    • ,
    • ,
    • ,
    • со значением , или .

Любой другой запрос считается «непростым». Например, запрос с методом или с HTTP-заголовком не соответствует условиям.

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

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

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

Когда мы пытаемся сделать непростой запрос, браузер посылает специальный предварительный запрос («предзапрос», по англ. «preflight»), который спрашивает у сервера – согласен ли он принять такой непростой запрос или нет?

И, если сервер явно не даёт согласие в заголовках, непростой запрос не посылается.

Далее мы разберём конкретные детали.

Получение нового содержимого

Другой ситуацией, в которой можно использовать объект XMLHttpRequest, будет загрузка нового содержимого в страницу. Например, новостная статья может сопровождаться несколькими фотографиями, но одновременно отображается только одна из них. Чтобы отобразить другую фотографию, пользователь нажимает соответствующую кнопку, а JavaScript-код получает ее и вставляет вместо предыдущей. Этот же способ можно использовать для показа изображений в слайд-шоу.

Пример такого слайд-шоу показан на рисунке ниже:

Содержимое этой страницы состоит из нескольких отдельных слайдов. Управление слайдами осуществляется щелчком по ссылкам «Пред» и «След», в результате чего загружается новое изображение со своей подписью. Запрос нового содержимого осуществляется посредством объекта XMLHttpRequest.

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

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

Для создания примера, показанного на рисунке, сначала нам нужно выделить место для динамического содержимого. Для этого мы используем элемент <div>, который создает нам рамку с золотистым обрамлением и двумя ссылками внизу:

Ссылки вызывают функцию previousSlide() или nextSlide() в зависимости от направления просмотра слайдов. В обеих функциях используется счетчик, начальное значение которого равно 0, увеличивается до 5 и начинает новый цикл со значения 1. Код функций nextSlide() и previousSlide() выглядит следующим образом:

Обе функции используют еще одну функцию, goToNewSlide(), которая в действительности и выводит новое изображение:

Последний шаг в клиентской части — это скопировать полученные данные и элемент <div>, который отображает текущий слайд:

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

Чтобы придать этому примеру больше шика, изображения можно менять, используя эффект перехода. Например, новое изображение может постепенно проявляться, в то время как старое постепенно исчезает. Это одно из достоинств динамических страниц, использующих объект XMLHttpRequest — они могут полностью управлять способом представления содержимого.

Ошибка в скрипте с другого источника

Есть правило: скрипты с одного сайта не могут получить доступ к содержимому другого сайта. Например, скрипт с не может прочитать почту пользователя на .

Или, если быть более точным, один источник (домен/порт/протокол) не может получить доступ к содержимому с другого источника. Даже поддомен или просто другой порт будут считаться разными источниками, не имеющими доступа друг к другу.

Это правило также касается ресурсов с других доменов.

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

Для примера давайте возьмём мини-скрипт , который состоит из одного-единственного вызова функции, которой не существует:

//  error.js
noSuchFunction();

Теперь загрузим этот скрипт с того же сайта, на котором он лежит:

<script>
window.onerror = function(message, url, line, col, errorObj) {
  alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="/article/onload-onerror/crossorigin/error.js"></script>

Мы видим нормальный отчёт об ошибке:

А теперь загрузим этот же скрипт с другого домена:

<script>
window.onerror = function(message, url, line, col, errorObj) {
  alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"></script>

Отчёт отличается:

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

Зачем нам могут быть нужны детали ошибки?

Существует много сервисов (и мы можем сделать наш собственный), которые обрабатывают глобальные ошибки при помощи , сохраняют отчёт о них и предоставляют доступ к этому отчёту для анализа. Это здорово, потому что мы можем увидеть реальные ошибки, которые случились у наших пользователей. Но если скрипт — с другого домена, то информации об ошибках в нём почти нет, как мы только что видели.

Похожая кросс-доменная политика (CORS) внедрена и в отношении других ресурсов.

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

Существует три уровня кросс-доменного доступа:

  1. Атрибут отсутствует — доступ запрещён.
  2. — доступ разрешён, если сервер отвечает с заголовком со значениями или наш домен. Браузер не отправляет авторизационную информацию и куки на удалённый сервер.
  3. — доступ разрешён, если сервер отвечает с заголовками со значением наш домен и . Браузер отправляет авторизационную информацию и куки на удалённый сервер.

В нашем случае атрибут отсутствовал. Поэтому кросс-доменный доступ был запрещён. Давайте добавим его.

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

Если куки нас не волнуют, тогда смело выбираем :

<script>
window.onerror = function(message, url, line, col, errorObj) {
  alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script *!*crossorigin="anonymous"*/!* src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"></script>

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

CORS для простых запросов

В кросс-доменный запрос браузер автоматически добавляет заголовок , содержащий домен, с которого осуществлён запрос.

В случае запроса на с заголовки будут примерно такие:

Сервер должен, со своей стороны, ответить специальными заголовками, разрешает ли он такой запрос к себе.

Если сервер разрешает кросс-доменный запрос с этого домена – он должен добавить к ответу заголовок , содержащий домен запроса (в данном случае «javascript.ru») или звёздочку .

Только при наличии такого заголовка в ответе – браузер сочтёт запрос успешным, а иначе JavaScript получит ошибку.

То есть, ответ сервера может быть примерно таким:

Если нет, то браузер считает, что разрешение не получено, и завершает запрос с ошибкой.

При таких запросах не передаются куки и заголовки HTTP-авторизации. Параметры и в методе игнорируются. Мы рассмотрим, как разрешить их передачу, чуть далее.

Что может сделать хакер, используя такие запросы?

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

Действительно, злая страница может сформировать любой GET/POST-запрос и отправить его, но без разрешения сервера ответа она не получит.

А без ответа такой запрос, по сути, эквивалентен отправке формы GET/POST, причём без авторизации.

Итого

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

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

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

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

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

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

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

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

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

Adblock
detector