103

Is it possible to post data to JsonP? Or does all data have to be passed in the querystring as a GET request?

I have alot of data that I need to send to the service, cross domain, and it is too large to send via the querystring

What are the options for getting around this?

Elnur Abdurrakhimov
  • 44,533
  • 10
  • 148
  • 133
ChrisCa
  • 10,876
  • 22
  • 81
  • 118

7 Answers7

84

It is not possible to do an asynchronous POST to a service on another domain, due to the (quite sensible) limitation of the same origin policy. JSON-P only works because you're allowed to insert <script> tags into the DOM, and they can point anywhere.

You can, of course, make a page on another domain the action of a regular form POST.

Edit: There are some interesting hacks out there if you're willing to go to a lot of effort inserting hidden <iframe>s and mucking about with their properties.

friedo
  • 65,762
  • 16
  • 114
  • 184
  • You mentioned that an "asynchronous POST" is not possible....then can I do a synchronous POST? – Mark Mar 11 '11 at 06:01
  • 4
    @mark "synchronous POST" means submitting a form that uses
    – Steven Kryskalla Jun 03 '11 at 22:00
  • 8
    This is not quite true. You certainly can do `POST` requests to other domains as long as both that domain and your browser support `CORS`. But it is totally true that `POST` and `JSONP` are not compatible. – hippietrail Feb 28 '12 at 12:24
  • 2
    JSONP is implemented by inserting ` – friedo May 10 '12 at 00:20
  • 1
    (in general - ) It is(!) possible to do an asynchronous POST to a service on another domain. the limitation is on the response. the limitation also is on JSONP request. – Royi Namir May 29 '14 at 06:12
  • @friedo if you modify the script for jsonp to add a defer tag on the script it will become async. – HellBaby Apr 14 '15 at 07:01
  • http://ajaxian.com/archives/how-to-make-xmlhttprequest-calls-to-another-server-in-your-domain is now a redirect that goes nowhere – John Vandenberg Mar 06 '22 at 01:20
21

If you need to send a lot of data cross-domain. I usually create a service that you can call in two steps:

  1. First the client do a FORM submit (post allowed cross domain). The service stores the input in the session on the server (using the GUID as key). (the client creates a GUID and send's it as a part of the input)

  2. Then the client do a normal script-inject (JSONP) as a parameter you use the same GUID as you used in the FORM post. The service processes the input from the session and returns the data in the normal JSONP-fashion. After this the session is destroyed.

This of course relies on that you write the server-backend.

Per
  • 211
  • 2
  • 2
  • 1
    Tried your aproach. Worked for FF14 and Chrome20. Opera11 and IE9 did just not transfer the post. (Checked it with their debug tools and listened on the server at the other end) Maybe related to the IE's disability is this question: http://stackoverflow.com/questions/10395803/cross-domain-post-request-ajax-in-internet-explorer Chrome complaint in the console, but still did the POST: XMLHttpRequest cannot load http://localhost:8080/xxx Origin null is not allowed by Access-Control-Allow-Origin. – OneWorld Jul 30 '12 at 17:32
  • @OneWorld — You didn't do what the answer said. `XMLHttpRequest` shouldn't be involved at all. Per's answer uses a regular form submission to make the POST request, then a script element injection to make the GET request. – Quentin Jun 05 '14 at 09:46
7

I know this is serious necromancy, but I thought I'd post my implementation of JSONP POST using jQuery, which I'm successfully using for my JS widget (this is used for customer registration and login):

Basically, I'm using an IFrame approach, as suggested in the accepted answer. What I'm doing differently is after sending the request, I'm watchin, if the form can be reached in the iframe, using a timer. When the form cannot be reached, it means the request has returned. Then, I'm using a normal JSONP request to query for the status of the operation.

I hope that someone finds it useful. Tested in >=IE8, Chrome, FireFox and 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;
}
W.B.
  • 5,445
  • 19
  • 29
4

Well generally JSONP is implemented by adding a <script> tag to the calling document, such that the URL of the JSONP service is the "src". The browser fetches script source with an HTTP GET transaction.

Now, if your JSONP service is in the same domain as your calling page, then you could probably cobble something together with a simple $.ajax() call. If it's not in the same domain, then I'm not sure how it'd be possible.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • It's not in the same domain in this case. And I am assuming that only GET is possible, but wanted to check as I've only started reading about JsonP today and need to make some decisions on whether it is suitable for what I need – ChrisCa Apr 23 '10 at 14:26
  • 2
    If it's not in the same domain but it supports `CORS` then it will be possible as long as the browser also supports it. In these cases you will use plain `JSON` rather than `JSONP`. – hippietrail Feb 28 '12 at 12:26
  • Yes, @hippietrail 2 years makes a big difference :-) CORS definitely makes it possible, but of course it does require that the data source be set up appropriately. – Pointy Feb 28 '12 at 13:05
0

You could use a CORS Proxy using this project. It would direct all traffic to an endpoint on your domain and relay that information to an external domain. Since the browser is registering all requests to be on the same domain we are able to post JSON. NOTE: This also works with SSL certificates held on the server.

Eugene Scray
  • 263
  • 1
  • 3
  • 13
-1

There's a (hack) solution I've did it many times, you'll be able to Post with JsonP. (You'll be able to Post Form, bigger than 2000 char than you can use by GET)

Client application 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);
  }
});

JAVA:

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');

Doing like this, you are opening your server to any post request, you should re-secure this by providing ident or something else.

With this method, you could also change the request type from jsonp to json, both work, just set the right response content type

jsonp

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

json

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

Please not that you're server will no more respect the SOP (same origin policy), but who cares ?

Dimitri Kopriwa
  • 13,139
  • 27
  • 98
  • 204
  • This is not AJAX with CORS. AJAX implies that you're using XML. This is JSON[P] with CORS. JSONP is "JSON" with "Padding". If it's sending JSON data, wrapped with a function call for padding, then it's JSONP with CORS. You can use both JSON and JSONP data notations outside of just injecting ` – BrainSlugs83 Mar 24 '14 at 01:15
-6

It is possible, here is my solution:

In your javascript:

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

In your url.php:

echo "handleRequest(".$responseData.")";
nosemaj
  • 1
  • 1
  • 11
    In this case jQuery most likely turned your request into Get according to their documentation: Note: This will turn POSTs into GETs for remote-domain requests. http://api.jquery.com/jQuery.ajax/ – OneWorld Jul 30 '12 at 08:39