602

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

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

4

17 回答 17

399

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

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

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

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

当您在步骤 2 中执行 POST 时,您的浏览器将向服务器发送一个“OPTIONS”方法。这是浏览器的“嗅探”,以查看服务器是否适合您向其发布。如果请求来自“ http://from.com ”或“ https://from.com ”,服务器会以“Access-Control-Allow-Origin”响应,告诉浏览器可以 POST|GET|ORIGIN。由于服务器没有问题,浏览器将发出第二个请求(这次是 POST)。让您的客户端设置它发送的内容类型是一种很好的做法——因此您也需要允许这样做。

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

如果您这样做,请记住以下几点:

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

如果您控制远程服务器,您可能应该使用 CORS,如本答案中所述;它在 IE8 及更高版本以及所有最新版本的 FF、GC 和 Safari 中都受支持。(但在 IE8 和 9 中,CORS 不允许您在请求中发送 cookie。)

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

  1. 创建一个具有唯一名称的 iframe。(iframe 为整个浏览器使用全局命名空间,因此请选择其他网站不会使用的名称。)
  2. 构造一个带有隐藏输入的表单,以 iframe 为目标。
  3. 提交表格。

这是示例代码;我在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 存在于单独的域中。不允许来自不同域的帧相互通信;这是同源策略

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

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

于 2011-05-29T18:50:30.857 回答
49
  1. 创建一个 iFrame,
  2. 在其中放置一个带有隐藏输入的表单,
  3. 将表单的操作设置为 URL,
  4. 将 iframe 添加到文档
  5. 提交表格

伪代码

 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 的样式,使其隐藏并绝对定位。不确定浏览器是否允许跨站点发布,但如果是这样,这就是如何做到的。

于 2008-11-18T13:49:36.293 回答
24

把事情简单化:

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

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


示例脚本:

$.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
        }
    });
于 2014-10-03T17:01:45.480 回答
16

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

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;
}

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

于 2011-01-22T00:40:05.317 回答
9

检查http://taiyolab.com/mbtweet/scripts/twitterapi_call.jspost_method中的函数- 上面描述的 iframe 方法的一个很好的例子。

于 2010-02-24T22:03:35.440 回答
6
  1. 创建两个隐藏的 iframe(在 css 样式中添加“display: none;”)。使您的第二个 iframe 指向您自己域上的某些内容。

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

  3. 准备好后,将表单 submit() 设置为 POST。

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

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

于 2010-03-09T15:13:12.710 回答
6

One more important thing to note!!! In example above it's described how to use

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

JQuery 1.6 and lower has a bug with cross-domain XHR. According to Firebug no requests except OPTIONS were sent. No POST. At all.

Spent 5 hours testing/tuning my code. Adding a lot of headers on the remote server (script). Without any effect. But later, I've updated JQuery lib to 1.6.4, and everything works like a charm.

于 2012-01-25T17:13:33.303 回答
5

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

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

  1. 在“server.com”应用程序的 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. 在“server.com”上,我们将在我们将发布到的控制器(称为“Home”)上执行以下操作:

    [HttpPost]
    public JsonResult Save()
    {
        //Handle the post data...
    
        return Json(
            new
            {
                IsSuccess = true
            });
    }
    
  3. 然后从“caller.com”,将数据从表单(html id 为“formId”)发布到“server.com”,如下所示:

    $.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
            }
        });
    
于 2014-05-06T17:32:46.333 回答
4

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

sender.com 上的父母

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)
}
于 2014-06-28T06:23:14.157 回答
3

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

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

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

于 2009-08-19T18:39:46.033 回答
3

我认为最好的方法是使用 XMLHttpRequest(例如 jQuery 中的 $.ajax()、$.post())和跨域资源共享 polyfill 之一https://github.com/Modernizr/Modernizr/wiki/HTML5-跨浏览器-Polyfills#wiki-CORS

于 2011-10-04T09:49:18.667 回答
2

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

如果您对另一台服务器具有管理权限,那么您可以使用开源 Forge 项目来完成您的跨域 POST。Forge 提供了一个跨域 JavaScript XmlHttpRequest 包装器,它利用了 Flash 的原始套接字 API。POST 甚至可以通过 TLS 完成。

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

http://github.com/digitalbazaar/forge

于 2010-09-23T17:58:06.353 回答
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.');
}
});
于 2014-03-06T19:15:41.740 回答
1

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

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

于 2010-08-24T15:59:49.930 回答
1

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

这个怎么运作:

步骤1:服务器1:包含xdomain库并将跨域配置为slave:

<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)
于 2018-09-11T21:27:32.640 回答
1

CORS 适合您。CORS是“Cross Origin Resource Sharing”,是一种跨域请求的发送方式。现在XMLHttpRequest2和Fetch API都支持CORS,可以发送POST和GET请求

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

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

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


如果您使用的是现代浏览器(> IE9、Chrome、FF、Edge等),非常推荐您使用简单但美观的组件https://github.com/Joker-Jelly/catta。它没有依赖,Less大于 3KB,它支持 Fetch、AJAX 和 JSONP,具有相同的致命示例语法和选项。

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

它还支持一直导入到您的项目中,例如 ES6 模块、CommonJS 甚至<script>是 HTML。

于 2017-01-19T14:44:34.993 回答