Подтвердить что ты не робот

Использование PUT/POST/DELETE с помощью JSONP и jQuery

Я работаю над созданием RESTful API, который поддерживает междоменные запросы, поддержку JSON/JSONP и основной HTTP-метод (PUT/GET/POST/DELETE). Теперь, когда будет легко получить доступ к этому API через код на стороне сервера, было бы приятно разоблачить его на javascript. Из того, что я могу сказать, при выполнении запросов JSONP с jQuery он поддерживает только метод GET. Есть ли способ сделать запрос JSONP, используя POST/PUT/DELETE?

В идеале я хотел бы сделать это из jQuery (через плагин, если ядро ​​этого не поддерживает), но я также возьму простое решение для JavaScript. Любые ссылки на рабочий код или способы его кодирования будут полезны, спасибо.

4b9b3361

Ответ 1

На самом деле - есть способ поддержать запросы POST. И нет необходимости в сервере PROXI - только небольшая страница утилиты HTML, описанная ниже.

Здесь вы получаете эффективный междоменный вызов POST, включая прикрепленные файлы и многочастные и все:)

Здесь сначала находятся шаги в понимании идеи, после чего - найти образец реализации.

Как реализуется JSONP jQuery и почему он не поддерживает запросы POST?

В то время как традиционный JSONP реализуется путем создания элемента script и добавления его в DOM - что приводит к тому, что браузер Firefox запускает HTTP-запрос для извлечения источника для тега, а затем выполняет его как JavaScript, HTTP запросить, что браузер срабатывает, просто GET.

Что не ограничено запросами GET?

ФОРМА. Отправьте FORM, указав action междоменный сервер. Тег FORM можно полностью создать с помощью script, заполненного всеми полями, используя script, установить все необходимые атрибуты, ввести в DOM, а затем отправить - все с помощью script.

Но как мы можем отправить FORM без обновления страницы?

Мы указываем форму target на IFRAME на той же странице. IFRAME также может быть создан, установлен, назван и введен в DOM с помощью script.

Но как мы можем скрыть эту работу от пользователя? Мы будем содержать как FORM, так и IFRAME в скрытом DIV, используя style="display:none"

(и здесь самая сложная часть метода, будьте терпеливы)

Но IFRAME из другого домена не может вызвать обратный вызов в документе верхнего уровня. Как преодолеть это?

Действительно, если ответ из предложения FORM представляет собой страницу из другого домена, любая связь script между страницей верхнего уровня и страницей в IFRAME приводит к "доступу к отказам". Таким образом, сервер не может выполнить обратный вызов с помощью script. Что может сделать сервер? Перенаправление. Сервер может перенаправлять на любую страницу - включая страницы того же домена, что и страницы верхнего уровня - страницы, которые могут вызвать обратный вызов для нас.

Как перенаправить сервер?

двумя способами:

Итак, у меня другая страница? Как это мне помогает?

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

Все, что нужно сделать, это вызвать обратный вызов в документе верхнего уровня с данными ответа с сервера. Скрипт на стороне клиента имеет доступ ко всем URL-частям, и сервер может поместить в него ответ, закодированный как часть его, а также имя вызываемого вызова, который должен быть вызван. Средства - эта страница может быть статической и HTML-страницей и не должна быть динамической серверной страницей:)

Эта страница служебных программ отобразит информацию из URL-адреса, который она запускает, в частности, в моей реализации ниже - параметры Query-String (или вы можете написать свою собственную реализацию с использованием идентификатора привязки - то есть, части URL-адреса справа на "#" знак). И так как эта страница статична - ее даже можно кэшировать:)

Не будет добавлять для каждого запроса POST DIV, script и IFRAME в конечном итоге утечку памяти?

Если вы оставите его на странице - это будет. Если вы почистите после вас - это не произойдет. Все, что нам нужно сделать, это предоставить идентификатор DIV, который мы можем использовать для развертывания DIV и FORM и IFRAME внутри него всякий раз, когда ответ приходит с сервера или истекает время.

Что мы получаем?

Эффективно вызов междоменной POST, включая прикрепленные файлы и многочастные и все:)

Каковы пределы?

  • Ответ сервера ограничен тем, что подходит для перенаправления.
  • Сервер должен ВСЕГДА возвращать REDIRECT в POST-запросы. Это включает ошибки 404 и 500. Альтернативно - создайте тайм-аут на клиенте непосредственно перед запуском запроса, чтобы у вас была возможность обнаружить запросы, которые не были возвращены.
  • не все могут понять все это и все этапы. это своего рода работа на уровне инфраструктуры, но как только вы ее запускаете - это скалы:)

Могу ли я использовать его для вызовов PUT и DELETE?

Тег FORM не устанавливает и не удаляет. Но это лучше, чем ничего:)

Хорошо, поняла. Как это делается технически?

Что я делаю:

Я создаю DIV, создаю его как невидимый и добавляю в DOM. Я также даю ему идентификатор, который я могу очистить от DOM после того, как серверный ответ пришел (так же, как JQuery очищает его JSONP script tasgs - но DIV).

Затем я составлю строку, содержащую как IFRAME, так и FORM - со всеми атрибутами, свойствами и полями ввода и вставляя их в невидимый DIV. важно вставить эту строку в DIV только ПОСЛЕ того, как div находится в DOM. Если нет - он не будет работать во всех браузерах.

После этого - я получаю ссылку на ФОРМУ и отправлю ее. Просто запомните одну строку до этого - установите обратный вызов Timeout, если сервер не отвечает или отвечает неправильно.

Функция обратного вызова содержит код очистки. Он также вызывается таймером в случае отклика-ответа (и очищает таймер-таймер при поступлении ответа сервера).

Покажите мне код!

Ниже приведен фрагмент кода абсолютно "нейтральный" на "чистом" javascript и объявляет любую полезную утилиту, в которой он нуждается. Просто для упрощения объяснения идеи - все это работает в глобальном масштабе, однако оно должно быть немного более сложным...

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

В этом примере - предположим, что клиент работает на http://samedomain.com, и сервер работает на http://crossdomain.com.

Код script в документе верхнего уровня

//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,"&quot;");//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 может быть выражением Object 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 для всех моих почтовых запросов: все, что она делает, - это взять имя обратного вызова и параметры из Query-String с использованием кода на стороне клиента и вызвать его в родительском документе. Он может сделать это ТОЛЬКО, когда эта страница выполняется в ТОКОМ том же домене, что и страница, на которой был отправлен запрос! Важно: В отличие от файлов cookie - субдомены не учитываются! Он должен иметь тот же самый домен.

Это также делает его более эффективным, если эта страница утилиты не содержит ссылок на другие ресурсы, включая 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 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/

Приятель удачи, надеюсь, это поможет...

Ответ 2

Есть ли способ сделать запрос JSONP, используя POST/PUT/DELETE?

Нет. Нет.

Ответ 3

Нет. Рассмотрим, что такое JSONP: инъекция нового тега <script> в документе. Браузер выполняет запрос GET, чтобы вытащить script, на который указывает атрибут src. Нет способа указать какой-либо другой HTTP-глагол при этом.

Ответ 4

  • Вместо того, чтобы ударить наши головы с помощью метода JSONP, это на самом деле не будет поддерживаем метод POST по умолчанию, мы можем пойти на CORS. Это не приведет к большим изменениям в обычном способе программирования. Путем простого вызова JQuery Ajax мы можем перейти с перекрестными доменами.
  • В методе CORS вам необходимо добавить заголовки в файле сценариев на стороне сервера или на самом сервере (в удаленном домене) для включения этого доступа. Это очень надежно, поскольку мы можем предотвратить/ограничить домены, делающие нежелательные вызовы.
  • Его можно найти в wikipedia странице.