Jakiś czas temu opisywałem na czym polega podejśćie cross domain JSONP. Wiemy jak pobrać dane z serwisu umieszonego na innej domenie, natomiast pozostaje pytanie jak wysłać dane na inną domene? Oczywiście można przesłać dane poprzez parametry URL-a korzystając z JSONP (dynamiczne wstawianie tag-a script) lub dynamicznie wstawiając obrazek <img> do dokumentu z odpowiednio dostosowanym URL-em (większość serwisów zliczających statystyki korzysta właśnie z tej metody).
Niestety istnieje ograniczenie długości URL-a. Przyjmuje się iż nie powinno się konstruować URL-i dłuższych niż 2000 znaków. (ze względu na to iż Internet Explorer jest wstanie obsłużyc maxymalnie 2083 znaków). Co jeśli budujemy aplikacje której celem jest przesłanie np. opisów lub komentarzy od użytkowników do serwisu backendowego, gdzie dane znacznie mogą przekroczyć ograniczenie URL-a.
Otóż ‘trick’ jest bardzo prosty. Wykrozystuje się do tego <iframe>. Polega na dynamicznym dołączeniu iframe-a do dokumentu utworzeniu w iframe formularza oraz z POST-owania go na domenę naszego serwisu backendowego. Tworzymy iframe i dodajemy do dokumentu:
var iframe = document.createElement('iframe');
//możemy go ukryć od razu
iframe.style.display = none;
document.body.appendChild(iframe);
Niestety w IE nie mozemy manipulować iframe-m zaraz po dodaniu go do dokumentu. Musimy dalszy nasz kod wyłączyć z aktualnego wątku poprzez wykonanie setTimout-a (osobiście bardzo nie lubie korzystać z tej funkcji ale w tym przypadku nawet przy ustawieniu na 1ms pozwoli na pełne wyrenderowanie iframe-a w IE w pierwszej lini wykonania javascriptu).
setTimout(handleIframe,1);
Tworzymy naszą funkcje która obsługuje iframe-a. Musimy dotrzeć do referencji dokumetu w utworzonej ramce oraz. dynamicznie utworzyć formularz. Warto wiedzieć iż w IE5 i IE6 property contentDocument nie istnieje trzeba odwołać się do contentWindow a następnie do document.
function handleIframe(){
//pobieramy document
var doc = iframe.contentWindow || iframe.contentDocument;
if(doc.document){
doc = doc.document;
}
//tworzymy formularz w iframe-ie
var form = doc.createElement('form');
form.method = 'post';
form.action = 'http://domena.na.ktora.postujemy.com';
form.setAttribute('enctype','multipart/form-data');
//tworzymy input-a na dane
var input = doc.createElement('input');
input.type = 'text';
input.name = 'dane';
input.value = 'dane ktore chcemy przeslac';
form.appendChild(input);
//dodajemy formularz do iframe-a
doc.body.appendChild(form);
//tworzymy funkcje do obslugi przeladowania iframe-a
var iframeLoad = function(){
//wyciagamy obsluge zdazenia z akutalnego watku
//zeby np. Firefox nie pokazywal statusu ladowania
setTimeout(function(){
//cos robimy po przeladowaniu np. usuwamy iframe
},1)
}
//podpinamy handler do iframe-a
iframe.onload = function(){
iframeLoaded();
};
//podpinamy dla explorera
iframe.onreadystatechange = function(){
if(this.readyState === 'complete'){
iframeLoaded();
}
}
//wysylamy formularz
form.submit();
}
Własnie wysłaliśmy dane na inna domenę. Niestety istnieje poważne ograniczenie zwiazane z wysyłaniem formularza: nie jesteśmy w stanie odczytać odpowiedzi z serwera, iframe po prostu się przeładuje. Mamy jednak dostęp do zdażenia mowiącego o tym że został przeładowany. W takie sytuacji budując aplikacje możemy wykorzystac JSONP aby ‘zpytać’ aplikacje backendową o status poprzedniej operacji. Istotne jest iż przy tworzeniu funkcji obsługującej zdażenie przeładowania ramki trzeba pamiętać iż jeśli chcemy usunąć iframe warto tą akcje ‘wyciagnąć’ z głównego wątku korzystając z setTimeout-a ponieważ w przeciwnym wypadku np. Firefox bedzię pokazywał status ładowania strony a nie jest to dobry user experience.Tworząc tą funkcje warto wiedzieć również żę w IE należy podpiąć zdażenie do onreadystatechange w obiekcie iframe i następnie sprawdzić readyState gdzie przy wartośći ‘complete’ mamy pewność żę ramka się przeładowała.
Niestety za każdym razem gdy chcemy wysłać dane musimy powtórzyć tworzenie iframe-a ponieważ po przeładowaniu dokument pochodzi już z innej domeny i nie można nim manipulować. Mimo tych wszystkich niedogodności jak widać można wysłać dane na inną domene bez ograniczenia długości które dotyczących URL-a.
Gdzie z tego korzystać?
JSONP oraz crossdomain POST głównie wykorzystuje przy twożeniu różnorodnych api a tak naprawde aplikacji klienckich które mogą zostać umieszczone na dowolnej domenie w sieci. Dodatkowo można te podjeścia wykorzystać budująć aplikacje o rozproszoej architekturze backendowej kiedy nasza architektura przewiduje np. kilka serwisów backendowych występujących pod różnymi domenami. Również można wykorzystać podejście cross-domenowe przy wysyłaniu danych na niezależny serwis, aczkolwiek i tak warto się zastanowić czy w takim wypadku nie lepiej zmodyfikować naszej aplikacji serwerowej (na naszej domenie) żeby działała jako proxy i korzystać ze wszelkich udogonień zwykłego AJAX-a. Warto też wiedzieć iż tworząc aplikacje mobilne w javascripcie nie musimy wykorzystywać JSONP czy crossdomain POST-a ponieważ przeglądarki w telefonach komórkowych zezwalają na łączenie się z dowolną domeną w sieci.

[...] Prawda, że proste?Nie będę tutaj pisał przykładowego kodu, bo zostało to już zrobione przez Frontend.pl, gdzie też odsyłam zainteresowanych tematem.Cross-domain requests with jQueryNa sam koniec [...]
[...] http://frontend.pl/2010/06/cross-domain-post-czyli-jak-oszukac-przegladarke-cz-2/ [...]
Ostatnio zrobiłem coś podobnego, choć starałem się zachować interfejs XMLHttpRequest:
http://www.yarpo.pl/2011/03/23/ajax-w-oparciu-o-plywajaca-ramke/
setTimeout powinien spokojnie zadziałać nawet z wartością czasu ustawioną na 0. Interpreter wykona zadany callback przy pierwszej możliwości. Wydaje mi się, że technicznie 15ns i tak jest minimalną wartością dla IE, nie wiem jak inne przeglądarki.
Świetna sprawa! Czy istnieje jakieś rozwiązanie, które pozwoliłoby na skorzystanie z proponowanego XDomain-POST w reakcji na onbeforeunload albo onunload? Gryzę się z tym od dłuższego czasu ale nie udaje mi się wysłać danych przy unloadzie…