На самом деле - есть способ поддержки POST-запросов.
И нет необходимости в сервере PROXI - просто небольшая служебная HTML-страница, которая описана ниже.
Вот как вы получаете эффективный междоменный вызов POST, включая вложенные файлы, несколько частей и все:)
Вот сначала шаги в понимании идеи, затем - найти пример реализации.
Как реализован JSONP в jQuery и почему он не поддерживает запросы POST?
В то время как традиционный JSONP реализуется путем создания элемента script и добавления его в DOM - что приводит к тому, что браузер запускает HTTP-запрос для получения источника для тега, а затем выполняет его как JavaScript, HTTP-запрос, который браузер запускается просто GET.
Что не ограничивается запросами GET?
ФОРМА. Отправьте ФОРМУ, указав action
междоменный сервер.
Тег FORM может быть создан полностью с использованием сценария, заполнен всеми полями с использованием сценария, задан все необходимые атрибуты, введен в DOM, а затем отправлен - все с использованием сценария.
Но как мы можем отправить ФОРМУ без обновления страницы?
Мы указываем target
форму для IFRAME на той же странице.
IFRAME также может быть создан, установлен, назван и внедрен в DOM с использованием сценария.
Но как мы можем скрыть эту работу от пользователя?
Мы будем хранить как FORM, так и IFRAME в скрытом DIV, используя style="display:none"
(и вот самая сложная часть техники, наберитесь терпения)
Но IFRAME из другого домена не может вызвать обратный вызов в своем документе верхнего уровня. Как это побороть?
Действительно, если ответом от отправки в FORM является страница из другого домена, любой сценарий связи между страницей верхнего уровня и страницей в IFRAME приводит к «отказу в доступе». Таким образом, сервер не может выполнить обратный вызов, используя скрипт. Что может сделать сервер? перенаправлять . Сервер может перенаправить на любую страницу, включая страницы в том же домене, что и документ верхнего уровня, страницы, которые могут вызвать для нас обратный вызов.
Как перенаправить сервер?
двумя способами:
- Использование сценария на стороне клиента, например
<Script>location.href = 'some-url'</script>
- Использование HTTP-заголовка. Смотри: http://www.webconfs.com/how-to-redirect-a-webpage.php
Так я получаю другую страницу? Как это мне поможет?
Это простая служебная страница, которая будет использоваться во всех междоменных вызовах. На самом деле эта страница на самом деле является своего рода прокси, но это не сервер, а простая и статичная HTML-страница , которую может использовать любой пользователь с блокнотом и браузером.
Все, что нужно сделать этой странице - это вызвать обратный вызов в документе верхнего уровня с данными ответа от сервера. Сценарии на стороне клиента имеют доступ ко всем частям URL, и сервер может поместить туда свой ответ, закодированный как его часть, а также имя обратного вызова, который должен быть вызван. Значит - эта страница может быть статической и HTML-страницей и не обязательно должна быть динамической серверной страницей:)
Эта служебная страница будет извлекать информацию из URL-адреса, в котором она запущена, - в частности, в моей реализации ниже - параметров Query-String (или вы можете написать свою собственную реализацию, используя anchor-ID), то есть часть URL-адреса справа от "#" знак). А поскольку эта страница статична - ее можно даже кэшировать:)
Не добавит ли для каждого запроса POST DIV, SCRIPT и IFRAME со временем утечка памяти?
Если вы оставите это на странице - так и будет. Если вы уберете за собой - этого не будет. Все, что нам нужно сделать, это дать ID DIV, который мы можем использовать для определения DIV, а также FORM и IFRAME внутри него всякий раз, когда приходит ответ с сервера или происходит тайм-аут.
Что мы получаем?
Эффективно междоменный вызов POST, включая вложенные файлы и несколько частей и все:)
Каковы пределы?
- Ответ сервера ограничен тем, что входит в перенаправление.
- Сервер ВСЕГДА должен возвращать REDIRECT на запросы POST. Это включает 404 и 500 ошибок.
В качестве альтернативы - создайте тайм-аут на клиенте непосредственно перед отправкой запроса, чтобы у вас была возможность обнаружить запросы, которые не были возвращены.
- Не каждый может понять все это и все вовлеченные стадии. это своего рода работа на уровне инфраструктуры, но как только вы ее запустите - она рухнет:)
Можно ли использовать его для вызовов PUT и DELETE?
Тег FORM не ставит и не удаляет.
Но это лучше, чем ничего:)
Хорошо, понял концепцию. Как это сделано технически?
Что я делаю:
Я создаю DIV, стилизую его как невидимый и добавляю в DOM. Я также даю ему идентификатор, который я могу очистить от DOM после получения ответа сервера (так же, как JQuery очищает его задачи JSONP SCRIPT - но DIV).
Затем я создаю строку, содержащую как IFRAME, так и FORM - со всеми атрибутами, свойствами и полями ввода, и внедряю ее в невидимый DIV. важно вводить эту строку в DIV только ПОСЛЕ того, как div находится в DOM. Если нет - он не будет работать во всех браузерах.
После этого - я получаю ссылку на ФОРМУ и отправляю ее.
Просто запомните одну строку перед этим - для установки обратного вызова тайм-аута в случае, если сервер не отвечает или отвечает неправильно.
Функция обратного вызова содержит код очистки. Он также вызывается таймером в случае тайм-аута ответа (и очищает таймер тайм-аута при получении ответа сервера).
Покажите мне код!
Фрагмент кода ниже абсолютно «нейтрален» по отношению к «чистому» javascript и объявляет любую нужную ему утилиту. Просто для упрощения объяснения идеи - все это работает в глобальном масштабе, однако должно быть немного более изощренным ...
Организуйте его в соответствии с вашими функциями и параметрируйте то, что вам нужно, но убедитесь, что все части, которые должны видеть друг друга, работают в одной области:)
Для этого примера - предположим, что клиент работает на http://samedomain.com, а сервер работает на http://crossdomain.com.
Код сценария в документе верхнего уровня
//declare the Async-call callback function on the global scope
function myAsyncJSONPCallback(data){
//clean up
var e = document.getElementById(id);
if (e) e.parentNode.removeChild(e);
clearTimeout(timeout);
if (data && data.error){
//handle errors & TIMEOUTS
//...
return;
}
//use data
//...
}
var serverUrl = "http://crossdomain.com/server/page"
, params = { param1 : "value of param 1" //I assume this value to be passed
, param2 : "value of param 2" //here I just declare it...
, callback: "myAsyncJSONPCallback"
}
, clientUtilityUrl = "http://samedomain.com/utils/postResponse.html"
, id = "some-unique-id"// unique Request ID. You can generate it your own way
, div = document.createElement("DIV") //this is where the actual work start!
, HTML = [ "<IFRAME name='ifr_",id,"'></IFRAME>"
, "<form target='ifr_",id,"' method='POST' action='",serverUrl
, "' id='frm_",id,"' enctype='multipart/form-data'>"
]
, each, pval, timeout;
//augment utility func to make the array a "StringBuffer" - see usage bellow
HTML.add = function(){
for (var i =0; i < arguments.length; i++)
this[this.length] = arguments[i];
}
//add rurl to the params object - part of infrastructure work
params.rurl = clientUtilityUrl //ABSOLUTE URL to the utility page must be on
//the SAME DOMAIN as page that makes the request
//add all params to composed string of FORM and IFRAME inside the FORM tag
for(each in params){
pval = params[each].toString().replace(/\"/g,""");//assure: that " mark will not break
HTML.add("<input name='",each,"' value='",pval,"'/>"); // the composed string
}
//close FORM tag in composed string and put all parts together
HTML.add("</form>");
HTML = HTML.join(""); //Now the composed HTML string ready :)
//prepare the DIV
div.id = id; // this ID is used to clean-up once the response has come, or timeout is detected
div.style.display = "none"; //assure the DIV will not influence UI
//TRICKY: append the DIV to the DOM and *ONLY THEN* inject the HTML in it
// for some reason it works in all browsers only this way. Injecting the DIV as part
// of a composed string did not always work for me
document.body.appendChild(div);
div.innerHTML = HTML;
//TRICKY: note that myAsyncJSONPCallback must see the 'timeout' variable
timeout = setTimeout("myAsyncJSONPCallback({error:'TIMEOUT'})",4000);
document.getElementById("frm_"+id+).submit();
Сервер на междоменном
Ожидается, что ответ от сервера будет REDIRECTION, либо через HTTP-заголовок, либо путем записи тега SCRIPT. (перенаправление лучше, тэг SCRIPT легче отлаживать с точками останова JS).
Вот пример заголовка, предполагая значение rurl
сверху
Location: http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return
Обратите внимание, что
- значение аргумента
data
может быть выражением JavaScript Object-Literal или JSON, однако его лучше кодировать в URL-адресе.
- длина ответа сервера ограничена длиной URL-адреса, который может обрабатывать браузер.
Также - в моей системе на сервере есть значение по умолчанию для rurl
, так что этот параметр является необязательным. Но вы можете сделать это, только если ваше клиентское приложение и серверное приложение связаны друг с другом.
API для создания заголовка перенаправления:
http://www.webconfs.com/how-to-redirect-a-webpage.php
В качестве альтернативы вы можете сделать так, чтобы сервер записал в ответ следующее:
<script>
location.href="http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return"
</script>
Но HTTP-заголовки будут считаться более чистыми;)
Страница утилит в том же домене, что и документ верхнего уровня
Я использую ту же служебную страницу, что и rurl
, для всех моих запросов на публикацию: все, что он делает, это берет имя обратного вызова и параметры из строки запроса, используя код на стороне клиента, и вызывает его в родительском документе. Это может сделать это ТОЛЬКО , когда эта страница работает в ТОЧНОМ домене, который совпадает со страницей, которая запустила запрос! Важно: В отличие от куки - субдомены не учитываются !! Он должен иметь точно такой же домен.
Это также делает его более эффективным, если эта служебная страница не содержит ссылок на другие ресурсы, включая библиотеки JS.Так что эта страница является простым JavaScript.Но вы можете реализовать его так, как вам нравится.
Вот страница респондента, которую я использую, URL которой находится в rurl
запроса POST (в примере: http://samedomain.com/utils/postResponse.html)
<html><head>
<script type="text/javascript">
//parse and organize all QS parameters in a more comfortable way
var params = {};
if (location.search.length > 1) {
var i, arr = location.search.substr(1).split("&");
for (i = 0; i < arr.length; i++) {
arr[i] = arr[i].split("=");
params[arr[i][0]] = unescape(arr[i][1]);
}
}
//support server answer as JavaScript Object-Literals or JSON:
// evaluate the data expression
try {
eval("params.data = " + params.data);
} catch (e) {
params.data = {error: "server response failed with evaluation error: " + e.message
,data : params.data
}
}
//invoke the callback on the parent
try{
window.parent[ params.callback ](params.data || "no-data-returned");
}catch(e){
//if something went wrong - at least let's learn about it in the
// console (in addition to the timeout)
throw "Problem in passing POST response to host page: \n\n" + e.message;
}
</script>
</head><body></body></html>
Это не большая автоматизация и «готовая» библиотека, такая как jQuery, которая включает в себя некоторую «ручную» работу - но она обладает очарованиемсозданные библиотеки - вы также можете проверить Dojo Toolkit , чтобы при последней проверке (около года назад) они имели собственную реализацию для того же механизма.http://dojotoolkit.org/
Удачи, приятель, надеюсь, это поможет ...