关于ajax:如何通过JavaScript发送跨域POST请求?

How do I send a cross-domain POST request via JavaScript?

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

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


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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    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。当你继续阅读时,这将变得清晰......

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $.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验证了这个功能。

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

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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来解决同源策略的问题。和/或许多库中的一个允许您在旧版浏览器中发送跨域跨框架消息:

    • 炮眼
    • XSSInterface
    • EasyXDM
    • jQuery PostMessage插件

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


  • 创建一个iFrame,
  • 在隐藏输入中放入一个表单,
  • 将表单的操作设置为URL,
  • 将iframe添加到文档中
  • 提交表格
  • 伪代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     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的样式,隐藏和绝对定位。不确定浏览器是否允许跨站点发布,但如果是这样,这是如何做到的。