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

IT技术 javascript ajax cross-domain
2021-01-10 23:24:53

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

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

6个回答

更新:在继续之前,每个人都应该阅读并理解关于 CORShtml5rocks 教程它很容易理解并且非常清楚。

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

简而言之,这里是如何完成从 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(jQuery 示例)设置您的跨域 POST:

    $.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”方法。这是浏览器的“嗅探”,以查看服务器是否在您 POST 时很酷。如果请求源自“ 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 的响应中需要它。
例如,它可以返回 html 吗?我需要返回 html 并且有些东西不起作用......
2021-03-13 23:24:53
这是 4 年前最后一次编辑的 - 现在可以在移动浏览器上使用吗?
2021-03-17 23:24:53
我已经试过了,但仍获得400 Bad RequestOPTIONS请求。并且在firefox第二个请求中POST从未提出过。:(
2021-03-24 23:24:53
是的,你应该可以。从来没有尝试过。您的服务器返回 200?您的服务器是否也在 OPTIONs 和 POST 请求中返回标头?我已经更新了我的答案,更详细地说明了这一点。确保您的服务器也使用正确的内容类型标头(如 text/html)进行响应。我的建议是使用谷歌浏览器,右键单击页面>检查元素。单击网络选项卡,然后查看 POST 和响应。应该给你关于发生了什么问题的信息。
2021-03-26 23:24:53
嗨@frankpinto 它适用于移动设备还是您使用了不同的方法?
2021-03-26 23:24:53

如果您控制远程服务器,则可能应该使用 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 期间的响应。否则会导致安全问题。

您需要将 form.target 设置为某些内容,否则浏览器将从您的站点导航到表单操作 URL。此外,字符串必须是唯一的;如果有其他框架或窗口使用相同的名称,则表单可以发布到该窗口而不是您的 iframe。但它必须有多独特?可能不是很。出轨的几率很小。耸耸肩
2021-03-21 23:24:53
@Nawaz 正如我在回答中所说,您必须进行跨域跨框架通信才能在您的网页中获得结果。它要求您控制远程 Web 服务器,以便您可以修改其响应以允许与您的网页进行通信。(一方面,服务器需要回复 HTML;如果服务器回复原始 XML,则无法进行跨帧通信。)
2021-04-01 23:24:53
@Andrus 您可以读取 POST 的结果,但前提是您控制了服务器!在那个答案中看到,它说“在发送者[客户端]上做X,在接收者[服务器]上做Y”。如果您不控制接收器/服务器,则无法执行 Y,因此无法读取 POST 的结果。
2021-04-03 23:24:53
+1 - 如果您无权访问服务器,这是我找到的最佳解决方案
2021-04-06 23:24:53
@VojtechB 不,那将是安全漏洞。
2021-04-08 23:24:53
  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 的样式,使其隐藏并绝对定位。不确定浏览器是否允许跨站点发布,但如果是这样,这就是这样做的方法。

2021-03-18 23:24:53
实际上,这有点不准确,因为 ifr.appendChild(frm); 不管用。iframe 是对 window 对象的引用,它不存在 appendChild 方法。您需要先获取 iframe 中的文档节点。这需要功能检测才能跨浏览器工作。
2021-03-24 23:24:53
尝试在响应的 body 标记中设置 onload 到 JavaScript 函数,该函数使用响应字符串调用父级中的函数。
2021-03-25 23:24:53
问题!iframe 中收到的响应位于不同的域,因此主窗口无法访问它,iframe 也无法访问主窗口。所以这个解决方案似乎只适合做 POST,但之后你无法解析响应:(
2021-03-30 23:24:53
这个答案对我不起作用;我在下面发布了我自己的变体。
2021-03-31 23:24:53

把事情简单化:

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

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


示例脚本:

$.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
        }
    });
crossDomain: true奇怪的是与真正的跨域请求完全无关。如果请求是跨域的,jquery 会自动将其设置为 true。
2021-03-31 23:24:53

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

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

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