Prześlij dane do JsonP

102

Czy można wysyłać dane do JsonP? A może wszystkie dane muszą być przekazywane w zapytaniu jako żądanie GET?

Mam dużo danych, które muszę wysłać do usługi, między domenami i są one zbyt duże, aby wysłać je za pośrednictwem zapytania

Jakie są możliwości obejścia tego problemu?

ChrisCa
źródło

Odpowiedzi:

83

Nie jest możliwe wykonanie asynchronicznej POSTusługi w innej domenie ze względu na (całkiem rozsądne) ograniczenie tej samej zasady pochodzenia . JSON-P działa tylko dlatego, że możesz wstawiać<script> tagi do DOM i mogą one wskazywać gdziekolwiek.

Możesz oczywiście uczynić stronę w innej domenie akcją zwykłego formularza POST.

Edycja : Istnieje kilka interesujących hacków , jeśli chcesz włożyć dużo wysiłku, wstawiając ukryte <iframe>litery i manipulując ich właściwościami.

Friedo
źródło
Wspomniałeś, że „asynchroniczny POST” nie jest możliwy… czy mogę wykonać synchroniczny POST?
Mark
4
@mark „synchroniczny POST” oznacza przesłanie formularza, który używa <form method = "post" action = "http: // ... / ...">
Steven Kryskalla
8
To nie jest do końca prawdą. Z pewnością możesz wysyłać POSTżądania do innych domen, o ile zarówno ta domena, jak i Twoja przeglądarka obsługują CORS. Ale to całkowicie prawda POSTi JSONPnie są kompatybilne.
hippietrail
2
JSONP jest implementowane poprzez wstawianie <script>tagów wskazujących na inną domenę. Jedynym sposobem wykonania żądań POST w przeglądarce jest użycie formularzy HTML lub XMLHttpRequest.
Friedo
1
(ogólnie -) Jest (!) możliwe wykonanie asynchronicznego POST do usługi w innej domenie. Ograniczenie dotyczy odpowiedzi. ograniczenie jest również na żądanie JSONP.
Royi Namir
20

Jeśli potrzebujesz przesłać dużo danych między domenami. Zwykle tworzę usługę, do której możesz zadzwonić w dwóch krokach:

  1. Najpierw klient przesyła FORMULARZ (przesyłanie dozwolone między domenami). Usługa przechowuje dane wejściowe sesji na serwerze (używając identyfikatora GUID jako klucza). (klient tworzy identyfikator GUID i wysyła go jako część danych wejściowych)

  2. Następnie klient wykonuje zwykłe wstrzyknięcie skryptu (JSONP) jako parametr, którego używasz tego samego identyfikatora GUID, który został użyty w poście FORMULARZA. Usługa przetwarza dane wejściowe z sesji i zwraca dane w normalnym trybie JSONP. Po tym sesja jest niszczona.

Zależy to oczywiście od tego, że napiszesz zaplecze serwera.

Za
źródło
1
Wypróbowałem twoje podejście. Pracował dla FF14 i Chrome20. Opera11 i IE9 po prostu nie przesłały postu. (Sprawdziłem to za pomocą ich narzędzi do debugowania i odsłuchałem na serwerze na drugim końcu) Być może związane z niepełnosprawnością IE jest to pytanie: stackoverflow.com/questions/10395803/… skarga Chrome w konsoli, ale nadal POST: XMLHttpRequest nie może load localhost: 8080 / xxx Origin null nie jest dozwolone przez Access-Control-Allow-Origin.
OneWorld
@OneWorld - Nie zrobiłeś tego, co mówi odpowiedź. XMLHttpRequestnie powinien być w ogóle zaangażowany. W odpowiedzi Per wykorzystuje zwykłe przesłanie formularza do żądania POST, a następnie wstrzyknięcie elementu skryptu w celu wykonania żądania GET.
Quentin,
7

Wiem, że to poważna nekromancja, ale pomyślałem, że opublikuję moją implementację JSONP POST przy użyciu jQuery, którego z powodzeniem używam do mojego widgetu JS (służy do rejestracji i logowania klienta):

Zasadniczo używam podejścia IFrame, zgodnie z sugestią zawartą w zaakceptowanej odpowiedzi. Inaczej robię po wysłaniu zapytania, obserwuję, czy do formularza można dotrzeć w ramce iframe, używając timera. Gdy formularz nie jest dostępny, oznacza to, że żądanie zostało zwrócone. Następnie używam zwykłego żądania JSONP, aby zapytać o stan operacji.

Mam nadzieję, że komuś się przyda. Przetestowano w> = IE8, Chrome, FireFox i Safari.

function JSONPPostForm(form, postUrl, queryStatusUrl, queryStatusSuccessFunc, queryStatusData)
{
    var tmpDiv = $('<div style="display: none;"></div>');
    form.parent().append(tmpDiv);
    var clonedForm = cloneForm(form);
    var iframe = createIFrameWithContent(tmpDiv, clonedForm);

    if (postUrl)
        clonedForm.attr('action', postUrl);

    var postToken = 'JSONPPOST_' + (new Date).getTime();
    clonedForm.attr('id', postToken);
    clonedForm.append('<input name="JSONPPOSTToken" value="'+postToken+'">');
    clonedForm.attr('id', postToken );
    clonedForm.submit();

    var timerId;
    var watchIFrameRedirectHelper = function()
    {
        if (watchIFrameRedirect(iframe, postToken ))
        {
            clearInterval(timerId);
            tmpDiv.remove();
            $.ajax({
                url:  queryStatusUrl,
                data: queryStatusData,
                dataType: "jsonp",
                type: "GET",
                success: queryStatusSuccessFunc
            });
        }
    }

    if (queryStatusUrl && queryStatusSuccessFunc)
        timerId = setInterval(watchIFrameRedirectHelper, 200);
}

function createIFrameWithContent(parent, content)
{
    var iframe = $('<iframe></iframe>');
    parent.append(iframe);

    if (!iframe.contents().find('body').length)
    {
        //For certain IE versions that do not create document content...
        var doc = iframe.contents().get()[0];
        doc.open();
        doc.close();
    }

    iframe.contents().find('body').append(content);
    return iframe;
}

function watchIFrameRedirect(iframe, formId)
{
    try
    {
        if (iframe.contents().find('form[id="' + formId + '"]').length)
            return false;
        else
            return true;
    }
    catch (err)
    {
        return true;
    }
    return false;
}

//This one clones only form, without other HTML markup
function cloneForm(form)
{
    var clonedForm = $('<form></form>');
    //Copy form attributes
    $.each(form.get()[0].attributes, function(i, attr)
    {
        clonedForm.attr(attr.name, attr.value);
    });
    form.find('input, select, textarea').each(function()
    {
        clonedForm.append($(this).clone());
    });

    return clonedForm;
}
WB
źródło
4

Ogólnie rzecz biorąc, JSONP jest implementowane przez dodanie pliku <script> tagu do wywołującego dokumentu, tak że adres URL usługi JSONP to „src”. Przeglądarka pobiera źródło skryptu z transakcją HTTP GET.

Teraz, jeśli Twoja usługa JSONP znajduje się w tej samej domenie, co strona wywołująca, prawdopodobnie możesz coś połączyć za pomocą prostego $.ajax()połączenia. Jeśli nie znajduje się w tej samej domenie, nie jestem pewien, jak byłoby to możliwe.

Spiczasty
źródło
W tym przypadku nie jest w tej samej domenie. A ja zakładając, że dostać tylko jest możliwe, ale chciałem sprawdzić jak mam tylko zacząłem czytać o jsonp dzisiaj i trzeba podjąć pewne decyzje o tym, czy jest ona odpowiednia dla czego potrzebuję
ChrisCa
2
Jeśli nie znajduje się w tej samej domenie, ale obsługuje CORS, będzie to możliwe, o ile przeglądarka również ją obsługuje. W takich przypadkach użyjesz zwykłego JSONzamiast JSONP.
hippietrail
Tak, @hippietrail 2 lata robi wielką różnicę :-) CORS zdecydowanie to umożliwia, ale oczywiście wymaga odpowiedniego skonfigurowania źródła danych.
Pointy
0

Przy użyciu tego projektu można użyć serwera proxy CORS . Skierowałoby cały ruch do punktu końcowego w Twojej domenie i przekazałby te informacje do domeny zewnętrznej. Ponieważ przeglądarka rejestruje wszystkie żądania w tej samej domenie, możemy wysyłać JSON. UWAGA: Działa to również z certyfikatami SSL przechowywanymi na serwerze.

Eugene Scray
źródło
-1

Istnieje (hack) rozwiązanie. Robiłem to wiele razy, będziesz mógł publikować za pomocą JsonP. (Będziesz mógł wysłać formularz, większy niż 2000 znaków, niż możesz użyć przez GET)

Aplikacja kliencka Javascript

$.ajax({
  type: "POST", // you request will be a post request
  data: postData, // javascript object with all my params
  url: COMAPIURL, // my backoffice comunication api url
  dataType: "jsonp", // datatype can be json or jsonp
  success: function(result){
    console.dir(result);
  }
});

JAWA:

response.addHeader( "Access-Control-Allow-Origin", "*" ); // open your api to any client 
response.addHeader( "Access-Control-Allow-Methods", "POST" ); // a allow post
response.addHeader( "Access-Control-Max-Age", "1000" ); // time from request to response before timeout

PHP:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Max-Age: 1000');

Robiąc w ten sposób, otwierasz swój serwer na każde żądanie postu, powinieneś ponownie zabezpieczyć to, podając identyfikator lub coś innego.

Dzięki tej metodzie możesz również zmienić typ żądania z jsonp na json, oba działają, po prostu ustaw odpowiedni typ zawartości odpowiedzi

jsonp

response.setContentType( "text/javascript; charset=utf-8" );

json

response.setContentType( "application/json; charset=utf-8" );

Proszę nie, że twój serwer nie będzie już przestrzegać SOP (tej samej polityki pochodzenia), ale kogo to obchodzi?

Dimitri Kopriwa
źródło
To nie jest AJAX z CORS. AJAX sugeruje, że używasz XML. To jest JSON [P] z CORS. JSONP to „JSON” z „Wypełnieniem”. Jeśli wysyła dane JSON, opakowane wywołaniem funkcji do wypełnienia, to jest to JSONP z CORS. Możesz używać zarówno notacji danych JSON, jak i JSONP poza samym wstrzykiwaniem <script>tagów do swojego HTML DOM (do diabła, możesz ich nawet używać w aplikacjach komputerowych, powiedzmy, że chcesz wysłać wiele żądań JSON do tego samego serwera i chcesz użyć nazwy funkcji na przykład jako identyfikator śledzenia żądań).
BrainSlugs83
-6

Jest to możliwe, oto moje rozwiązanie:

W twoim javascript:

jQuery.post("url.php",data).complete(function(data) {
    eval(data.responseText.trim()); 
});
function handleRequest(data){
    ....
}

W twoim url.php:

echo "handleRequest(".$responseData.")";
nosemaj
źródło
11
W tym przypadku jQuery najprawdopodobniej zamieniło twoje żądanie w Get zgodnie z ich dokumentacją: Uwaga: spowoduje to zmianę POST w GET dla żądań zdalnej domeny. api.jquery.com/jQuery.ajax
OneWorld