如何通过JavaScript发送跨域POST请求?

时间:2008-11-18 13:43:54

标签: javascript ajax cross-domain

如何通过JavaScript发送跨域POST请求?

注意 - 它不应该刷新页面,我需要在之后抓取并解析响应。

17 个答案:

答案 0 :(得分:374)

更新:在继续之前,每个人都应该阅读并理解CORS上的html5rocks tutorial。这很容易理解,也很清楚。

如果您控制正在POST的服务器,只需通过在服务器上设置响应标头来利用“跨源资源共享标准”。这个答案将在本主题的其他答案中讨论,但在我看来并不是很清楚。

简而言之,就是如何完成从from.com/1.html到to.com/postHere.php的跨域POST(以PHP为例)。注意:您只需为非Access-Control-Allow-Origin次请求设置OPTIONS - 此示例始终为较小的代码段设置所有标头。

  1. 在postHere.php中设置以下内容:

    switch ($_SERVER['HTTP_ORIGIN']) {
        case 'http://from.com': case 'https://from.com':
        header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
        header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
        header('Access-Control-Max-Age: 1000');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
        break;
    }
    

    这允许您的脚本进行跨域POST,GET和OPTIONS。当你继续阅读时,这将变得清晰......

  2. 从JS设置跨域POST(jQuery示例):

    $.ajax({
        type: 'POST',
        url: 'https://to.com/postHere.php',
        crossDomain: true,
        data: '{"some":"json"}',
        dataType: 'json',
        success: function(responseData, textStatus, jqXHR) {
            var value = responseData.someKey;
        },
        error: function (responseData, textStatus, errorThrown) {
            alert('POST failed.');
        }
    });
    
  3. 在步骤2中执行POST时,浏览器将向服务器发送“OPTIONS”方法。这是浏览器的“嗅探”,看看服务器是否很酷,你发布它。如果请求来自“http://from.com”或“https://from.com”,则服务器以“Access-Control-Allow-Origin”响应,告知浏览器可以POST | GET | ORIGIN。由于服务器没问题,浏览器会发出第二个请求(这次是POST)。优良做法是让您的客户端设置它发送的内容类型 - 所以您也需要允许它。

    MDN有一篇关于HTTP access control的精彩文章,详细介绍了整个流程的工作原理。根据他们的文档,它应该“在支持跨站点XMLHttpRequest的浏览器中工作”。然而,这有点误导,因为我 THINK 只有现代浏览器允许跨域POST。我只用safari,chrome,FF 3.6验证了这个功能。

    如果您这样做,请记住以下内容:

    1. 您的服务器每次操作必须处理2个请求
    2. 您必须考虑安全隐患。在做“Access-Control-Allow-Origin:*”
    3. 之前要小心
    4. 这不适用于移动浏览器。根据我的经验,他们根本不允许跨域POST。我测试了android,iPad,iPhone
    5. FF中有一个相当大的错误< 3.6如果服务器返回非400响应代码且存在响应主体(例如验证错误),则FF 3.6不会获得响应主体。这是一个巨大的痛苦,因为你不能使用良好的REST实践。请参阅bug here(它在jQuery下提交,但我的猜测是它的FF错误 - 似乎在FF4中得到修复)。
    6. 始终返回上面的标题,而不仅仅是OPTION请求。 FF在POST的响应中需要它。

答案 1 :(得分:118)

如果您控制远程服务器,you should probably use CORS, as described in this answer; IE8及更高版本支持它,以及所有最新版本的FF,GC和Safari。 (但在IE8和9中,CORS不允许您在请求中发送cookie。)

因此,如果您控制远程服务器,或者您必须支持IE7,或者您需要cookie并且您必须支持IE8 / 9,那么您可能希望使用iframe技术。

  1. 使用唯一名称创建iframe。 (iframe为整个浏览器使用全局命名空间,因此选择一个其他网站不会使用的名称。)
  2. 构建一个隐藏输入的表单,以iframe为目标。
  3. 提交表格。
  4. 这是示例代码;我在IE6,IE7,IE8,IE9,FF4,GC11,S5上进行了测试。

    function crossDomainPost() {
      // Add the iframe with a unique name
      var iframe = document.createElement("iframe");
      var uniqueString = "CHANGE_THIS_TO_SOME_UNIQUE_STRING";
      document.body.appendChild(iframe);
      iframe.style.display = "none";
      iframe.contentWindow.name = uniqueString;
    
      // construct a form with hidden inputs, targeting the iframe
      var form = document.createElement("form");
      form.target = uniqueString;
      form.action = "http://INSERT_YOUR_URL_HERE";
      form.method = "POST";
    
      // repeat for each parameter
      var input = document.createElement("input");
      input.type = "hidden";
      input.name = "INSERT_YOUR_PARAMETER_NAME_HERE";
      input.value = "INSERT_YOUR_PARAMETER_VALUE_HERE";
      form.appendChild(input);
    
      document.body.appendChild(form);
      form.submit();
    }
    

    小心!您将无法直接读取POST的响应,因为iframe存在于单独的域中。帧不允许来自不同域的相互通信;这是same-origin policy

    如果您控制远程服务器但不能使用CORS(例如,因为您使用的是IE8 / IE9,并且需要使用cookie),有很多方法可以解决同源策略,例如使用window.postMessage和/或许多库中的一个允许您在旧版浏览器中发送跨域跨框架消息:

    如果您不控制远程服务器,则无法读取POST的响应时间段。否则会引起安全问题。

答案 2 :(得分:46)

  1. 创建iFrame,
  2. 在表格中加入隐藏输入
  3. 将表单的操作设置为URL
  4. 将iframe添加到文档
  5. 提交表格
  6. 伪代码

     var ifr = document.createElement('iframe');
     var frm = document.createElement('form');
     frm.setAttribute("action", "yoururl");
     frm.setAttribute("method", "post");
    
     // create hidden inputs, add them
     // not shown, but similar (create, setAttribute, appendChild)
    
     ifr.appendChild(frm);
     document.body.appendChild(ifr);
     frm.submit();
    

    你可能想要设置iframe的样式,隐藏和绝对定位。不确定浏览器是否允许跨站点发布,但如果是这样,这就是如何做到的。

答案 3 :(得分:22)

保持简单:

  1. 跨域POST:
    使用crossDomain: true,

  2. 不应刷新页面:
    不,它不会刷新页面,因为当服务器发回响应时,将调用successerror异步回调。


  3. 示例脚本:

    $.ajax({
            type: "POST",
            url: "http://www.yoururl.com/",
            crossDomain: true,
            data: 'param1=value1&param2=value2',
            success: function (data) {
                // do something with server response data
            },
            error: function (err) {
                // handle your error logic here
            }
        });
    

答案 4 :(得分:15)

如果您可以访问所涉及的所有服务器,请将以下内容放在其他域中请求的页面的回复标题中:

PHP:

header('Access-Control-Allow-Origin: *');

例如,在Drupal的xmlrpc.php代码中,您可以这样做:

function xmlrpc_server_output($xml) {
    $xml = '<?xml version="1.0"?>'."\n". $xml;
    header('Connection: close');
    header('Content-Length: '. strlen($xml));
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/x-www-form-urlencoded');
    header('Date: '. date('r'));
    // $xml = str_replace("\n", " ", $xml); 

    echo $xml;
    exit;
}

这可能会产生安全问题,您应该确保采取适当的措施来验证请求。

答案 5 :(得分:9)

检查http://taiyolab.com/mbtweet/scripts/twitterapi_call.js中的post_method功能 - 这是上述iframe方法的一个很好的示例。

答案 6 :(得分:6)

  1. 创建两个隐藏的iframe(将“display:none;”添加到css样式)。将您的第二个iframe指向您自己域名中的内容。

  2. 创建一个隐藏的表单,将其方法设置为使用target =您的第一个iframe“post”,并可选择将enctype设置为“multipart / form-data”(我想你想要POST,因为你想要发送像图片一样的多部分数据?)

  3. 准备好后,将表格提交()POST。

  4. 如果您可以让其他域返回将使用Iframe(http://softwareas.com/cross-domain-communication-with-iframes)进行跨域通信的JavaScript,那么您很幸运,并且您也可以捕获响应。

  5. 当然,如果您想将服务器用作代理,则可以避免所有这些。只需将表单提交给您自己的服务器,服务器将请求代理到其他服务器(假设其他服务器未设置为通知IP差异),获取响应,并返回您喜欢的任何内容。

答案 7 :(得分:6)

还有一点需要注意! 在上面的example中,它描述了如何使用

$.ajax({
    type     : 'POST',
    dataType : 'json', 
    url      : 'another-remote-server',
    ...
});

JQuery 1.6及更低版本存在跨域XHR的错误。 据Firebug称,除了OPTIONS之外没有任何请求被发送。没有POST。完全没有。

花了5个小时测试/调整我的代码。在远程服务器(脚本)上添加大量标头。没有任何影响。 但后来,我将JQuery lib更新为1.6.4,一切都像魅力一样。

答案 8 :(得分:5)

如果您想在使用JQuery AJAX的ASP.net MVC环境中执行此操作,请按照下列步骤操作: (这是this线程提供的解决方案的摘要)

假设&#34; caller.com&#34;(可以是任何网站)需要发布到&#34; server.com&#34;(一个ASP.net MVC应用程序)

  1. 在&#34; server.com&#34;应用程序的Web.config添加以下部分:

      <httpProtocol>
          <customHeaders>
              <add name="Access-Control-Allow-Origin" value="*" />
              <add name="Access-Control-Allow-Headers" value="Content-Type" />
              <add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS" />
          </customHeaders>
      </httpProtocol>
    
  2. 在&#34; server.com&#34;上,我们将在控制器上进行以下操作(称为&#34; Home&#34;),我们将在其中发布:< / p>

    [HttpPost]
    public JsonResult Save()
    {
        //Handle the post data...
    
        return Json(
            new
            {
                IsSuccess = true
            });
    }
    
  3. 然后从&#34; caller.com&#34;,将表单中的数据(带有html id&#34; formId&#34;)发布到&#34; server.com&#34;如下:

    $.ajax({
            type: "POST",
            url: "http://www.server.com/home/save",
            dataType: 'json',
            crossDomain: true,
            data: $(formId).serialize(),
            success: function (jsonResult) {
               //do what ever with the reply
            },
            error: function (jqXHR, textStatus) {
                //handle error
            }
        });
    

答案 9 :(得分:3)

高级....您需要在服务器上设置cname,以便other-serve.your-server.com指向other-server.com。

您的页面动态创建一个不可见的iframe,它作为您对other-server.com的传输。然后,您必须通过JS从您的页面与other-server.com进行通信,并拥有将数据返回到您的页面的回调。

可能但需要来自your-server.com和other-server.com的协调

答案 10 :(得分:3)

还有一种方法(使用html5功能)。您可以使用托管在该其他域上的代理iframe,使用postMessage将消息发送到该iframe,然后iframe可以执行POST请求(在同一个域上)和postMessage返回并重新发布到父窗口。

发送者邮箱上的父母

var win = $('iframe')[0].contentWindow

function get(event) {
    if (event.origin === "http://reciver.com") {
        // event.data is response from POST
    }
}

if (window.addEventListener){
    addEventListener("message", get, false)
} else {
    attachEvent("onmessage", get)
}
win.postMessage(JSON.stringify({url: "URL", data: {}}),"http://reciver.com");
在reciver.com上

iframe

function listener(event) {
    if (event.origin === "http://sender.com") {
        var data = JSON.parse(event.data);
        $.post(data.url, data.data, function(reponse) {
            window.parent.postMessage(reponse, "*");
        });
    }
}
// don't know if we can use jQuery here
if (window.addEventListener){
    addEventListener("message", listener, false)
} else {
    attachEvent("onmessage", listener)
}

答案 11 :(得分:3)

我认为最好的方法是使用XMLHttpRequest(例如jQuery中的$ .ajax(),$ .post())和一个跨源资源共享polyfills https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills#wiki-CORS

答案 12 :(得分:2)

我知道这是一个老问题,但我想分享我的方法。我使用cURL作为代理,非常简单和一致。创建一个名为submit.php的php页面,并添加以下代码:

<?

function post($url, $data) {
$header = array("User-Agent: " . $_SERVER["HTTP_USER_AGENT"], "Content-Type: application/x-www-form-urlencoded");
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}

$url = "your cross domain request here";
$data = $_SERVER["QUERY_STRING"];
echo(post($url, $data));

然后,在你的js(这里是jQuery):

$.ajax({
type: 'POST',
url: 'submit.php',
crossDomain: true,
data: '{"some":"json"}',
dataType: 'json',
success: function(responseData, textStatus, jqXHR) {
    var value = responseData.someKey;
},
error: function (responseData, textStatus, errorThrown) {
    alert('POST failed.');
}
});

答案 13 :(得分:2)

这是一个老问题,但是一些新技术可能会帮助某人。

如果您具有对其他服务器的管理访问权限,则可以使用opensource Forge项目来完成跨域POST。 Forge提供了一个跨域JavaScript XmlHttpRequest包装器,它利用了Flash的原始套接字API。 POST甚至可以在TLS上完成。

您需要对要发布的服务器进行管理访问的原因是您必须提供允许从您的域进行访问的跨域策略。

http://github.com/digitalbazaar/forge

答案 14 :(得分:1)

应该可以使用YQL自定义表+ JS XHR,看看: http://developer.yahoo.com/yql/guide/index.html

我用它来做一些客户端(js)html抓取,工作正常 (我有一个完整的音频播放器,搜索互联网/播放列表/歌词/最后的fm信息,所有客户端js + YQL)

答案 15 :(得分:1)

CORS适合您。 CORS是“跨域资源共享”,是一种发送跨域请求的方式。现在XMLHttpRequest2和Fetch API都支持CORS,它可以发送POST和GET请求

但它有其限制。服务器需要具体声明 Access-Control-Allow-Origin ,并且不能设置为'*'。

如果您想要任何来源可以向您发送请求,您需要JSONP(还需要设置 Access-Control-Allow-Origin ,但可以是'*')

对于很多请求方式,如果你不知道如何选择,我认为你需要一个完整的功能组件来实现这一点。让我介绍一个简单的组件 https://github.com/Joker-Jelly/catta

如果您使用的是现代浏览器(&gt; IE9,Chrome,FF,Edge等),建议您使用简单但美观的组件 https://github.com/Joker-Jelly/catta 。它没有依赖,小于3KB,它支持Fetch,AJAX和JSONP,具有相同的致命样本语法和选项。

catta('./data/simple.json').then(function (res) {
  console.log(res);
});

它还支持导入项目的所有方式,例如ES6模块,CommonJS甚至是HTML中的<script>

答案 16 :(得分:1)

如果您可以访问跨域服务器,并且不想在服务器端进行任何代码更改,则可以使用名为“ xdomain”的库。

工作原理:

步骤1: 服务器1:包括xdomain库,并将跨域配置为从属服务器:

<script src="js/xdomain.min.js" slave="https://crossdomain_server/proxy.html"></script>

步骤2: 在跨域服务器上,创建一个proxy.html文件,并将服务器1作为主服务器:

proxy.html:
<!DOCTYPE HTML>
<script src="js/xdomain.min.js"></script>
<script>
  xdomain.masters({
    "https://server1" : '*'
  });
</script>

第3步:

现在,您可以从server1调用proxy.html作为端点的AJAX。这是绕过CORS要求。该库在内部使用iframe解决方案,该解决方案可与凭据和所有可能的方法(GET,POST等)一起使用。

查询ajax代码:

$.ajax({
        url: 'https://crossdomain_server/proxy.html',
        type: "POST",
        data: JSON.stringify(_data),
        dataType: "json",
        contentType: "application/json; charset=utf-8"
    })
    .done(_success)
    .fail(_failed)