将数据发布到JsonP


102

是否可以将数据发布到JsonP?还是所有数据都必须作为GET请求传递到查询字符串中?

我有很多数据需要跨域发送到服务,并且太大了,无法通过查询字符串发送

解决这个问题有哪些选择?

Answers:


83

POST由于相同来源策略的(相当明智的)限制,因此无法与另一个域上的服务进行异步。JSON-P之所以起作用,是因为允许您将<script>标签插入DOM,并且它们可以指向任何地方。

当然,您可以使用常规形式的POST来使另一个域上的页面。

编辑:如果您愿意花很多精力插入隐藏的s并对其属性进行处理,那么会有一些有趣的技巧<iframe>


您提到了“异步POST”是不可能的。...那么我可以进行同步POST吗?
标记

4
@mark“同步POST”表示提交使用<form method =“ post” action =“ http:// ... / ...”>
Steven Kryskalla 2011年

8
这不是真的。POST只要该域和您的浏览器都支持,您当然可以向其他域发出请求CORS。但它是完全真实的POSTJSONP不兼容。
hippietrail

2
JSONP是通过插入<script>指向另一个域的标签来实现的。在浏览器中执行POST请求的唯一方法是通过HTML表单或XMLHttpRequest。
Friedo 2012年

1
(通常-)可以对另一个域上的服务执行异步POST。限制在于响应。限制也针对JSONP请求。
罗伊·纳米尔

20

如果需要跨域发送大量数据。我通常会创建一个可以分两步调用的服务:

  1. 首先,客户进行FORM提交(允许跨域发布)。该服务将输入存储在服务器上的会话中(使用GUID作为键)。(客户端创建GUID并将其作为输入的一部分发送)

  2. 然后,客户端将使用与FORM帖子中使用的GUID相同的GUID作为参数进行普通脚本注入(JSONP)。该服务处理来自会话的输入,并以正常的JSONP时尚形式返回数据。在此之后,会话被破坏。

当然,这取决于您编写服务器后端。


1
尝试了您的方法。适用于FF14和Chrome20。Opera11和IE9只是不转移职位。(使用其调试工具进行了检查,然后在另一端的服务器上进行了侦听)可能与IE的残障有关的问题是:stackoverflow.com/questions/10395803/…控制台中的Chrome投诉,但POST仍然执行:XMLHttpRequest无法加载localhost:8080 / xxx Access-Control-Allow-Origin不允许使用原点null。
OneWorld 2012年

@OneWorld —您没有按照答案说的做。XMLHttpRequest根本不应该参与其中。Per的答案使用常规表单提交来发出POST请求,然后使用脚本元素注入来发出GET请求。
昆汀2014年

7

我知道这是严重的死法,但是我想我应该使用jQuery发布JSONP POST的实现,我已经成功地将其用于JS小部件(用于客户注册和登录):

基本上,我使用的是IFrame方法,如公认的答案所建议。我做的不同的是,发送请求后,我正在监视,如果可以使用计时器在iframe中访问表单。如果无法到达表格,则表示请求已返回。然后,我使用普通的JSONP请求来查询操作状态。

我希望有人觉得它有用。在> = IE8,Chrome,FireFox和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;
}

4

通常,JSONP是通过<script>在调用文档中添加标签来实现的,因此JSONP服务的URL为“ src”。浏览器通过HTTP GET事务获取脚本源。

现在,如果您的JSONP服务与调用页面位于同一域中,那么您可能可以通过简单的$.ajax()调用将某些内容拼凑起来。如果不在同一个域中,那么我不确定怎么可能。


在这种情况下,它不在同一域中。我以为只有GET是可行的,但是我想检查一下,因为我今天才开始阅读有关JsonP的文章,并且需要对它是否适合我的需求做出一些决定
ChrisCa 2010年

2
如果它不在同一个域中,但它支持,CORS那么只要浏览器也支持它就可以。在这些情况下,您将使用Plain JSON而不是JSONP
hippietrail 2012年

是的,@ hippietrail 2年有很大的不同:-) CORS确实有可能,但是当然需要正确设置数据源。
Pointy'2

0

您可以在此项目中使用CORS代理。它将所有流量定向到您域上的终结点,并将该信息中继到外部域。由于浏览器将所有请求都注册在同一域中,因此我们可以发布JSON。 注意:这也适用于服务器上保存的SSL证书。


-1

我已经做了很多次(黑客)解决方案,您可以使用JsonP进行发布。(您将能够张贴表格,大于2,000个字符,超过GET可以使用的字符)

客户端应用程序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');

这样做,您正在向任何发布请求打开服务器,您应该通过提供ident或其他方式来确保安全。

使用此方法,您还可以将请求类型从jsonp更改为json,两者都起作用,只需设置正确的响应内容类型

jsonp

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

json

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

请注意,您的服务器将不再尊重SOP(相同来源策略),但是谁在乎呢?


这不是带有CORS的AJAX。AJAX表示您正在使用XML。这是带有CORS的JSON [P]。JSONP是带有“ Padding”的“ JSON”。如果它正在发送JSON数据,并用一个用于填充的函数调用包装,则它是带有CORS的JSONP。除了将<script>标记注入HTML DOM 外,您还可以同时使用JSON和JSONP数据表示法(哎呀,您甚至可以在桌面应用程序中使用它们,说您想向同一服务器发出多个JSON请求,并且想要使用函数名作为请求跟踪ID)。
BrainSlugs83 2014年

-6

有可能,这是我的解决方案:

在您的JavaScript中:

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

在您的url.php中:

echo "handleRequest(".$responseData.")";

11
在这种情况下,jQuery很可能会根据其文档将您的请求转换为Get:注意:这会将POST转换为针对远程域请求的GET。api.jquery.com/jQuery.ajax
OneWorld
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.