【问题标题】:How do I send a cross-domain POST request via JavaScript?如何通过 JavaScript 发送跨域 POST 请求?
【发布时间】:2010-09-22 20:40:07
【问题描述】:

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

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

【问题讨论】:

  • 我想了解一些让您尝试执行此操作的用例。可以请您介绍一下吗?
  • 基本上我正在编写一个脚本,该脚本需要将一些文本从 HTML 文件发送到另一台服务器进行处理。
  • 您能否设置一个代理在服务器端执行此操作并为您的脚本提供结果?还是需要 100% JavaScript?

标签: javascript ajax cross-domain


【解决方案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)

【讨论】:

    【解决方案2】:

    保持简单:

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

    【讨论】:

    • crossDomain: true 奇怪地与真正的跨域请求完全无关。如果请求是跨域的,jquery 会自动将此设置为 true。
    【解决方案3】:

    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 甚至 HTML 中的&lt;script&gt;

    【讨论】:

      【解决方案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)
      }
      

      【讨论】:

      【解决方案5】:

      如果你控制远程服务器,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. 提交表单。

      这里是示例代码;我在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 的响应,句号。否则会导致安全问题。

      【讨论】:

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

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

      假设“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
                }
            });
        

      【讨论】:

        【解决方案7】:

        我知道这是一个老问题,但我想分享我的方法。我使用 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.');
        }
        });
        

        【讨论】:

          【解决方案8】:

          更新:在继续之前,每个人都应该阅读并理解 CORS 上的 html5rocks tutorial。通俗易懂,非常清晰。

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

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

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

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

          1. 您的服务器每次操作必须处理 2 个请求
          2. 您必须考虑安全隐患。在执行“Access-Control-Allow-Origin: *”之类的操作之前要小心
          3. 这不适用于移动浏览器。根据我的经验,他们根本不允许跨域 POST。我测试过安卓、iPad、iPhone
          4. FF here(它在 jQuery 下提交,但我猜它是一个 FF 错误 - 似乎已在 FF4 中修复)。
          5. 始终返回上面的标头,而不仅仅是在 OPTION 请求上。 FF 在 POST 的响应中需要它。

          【讨论】:

          • 它可以返回例如html吗?我需要返回 html,但有些东西不工作......
          • 是的,你应该可以。从来没有尝试过。您的服务器返回 200?您的服务器是否还返回 OPTIONs 和 POST 请求的标头?我已经更新了我的答案,并提供了更多详细信息。确保您的服务器也使用正确的内容类型标头进行响应(例如 text/html)。我的建议是使用谷歌浏览器,右键单击页面>检查元素。单击网络选项卡,然后观察 POST 和响应。应该给你关于出了什么问题的信息。
          • 我已经尝试过了,但仍然在OPTIONS 请求中获得400 Bad Request。在firefox 中,POST 的第二个请求永远不会发出。 :(
          • 这是 4 年前最后一次编辑 - 现在可以在移动浏览器上使用吗?
          • 嗨@frankpinto 它适用于移动设备还是您使用了不同的方法?
          【解决方案9】:

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

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

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

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

          【讨论】:

          • 哎呀,不是在 Opera 10.61 中。我最终决定这样做是在我的域上使用 PHP 代理。
          • 您是如何使用 PHP 代理的?你能指导我吗?
          • 查看下面的答案,例如伊万·德斯特(Ivan Durst)
          【解决方案10】:

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

          【讨论】:

            【解决方案11】:

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

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

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

            【讨论】:

              【解决方案12】:

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

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

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

              http://github.com/digitalbazaar/forge

              【讨论】:

                【解决方案13】:

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

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

                【讨论】:

                  【解决方案14】:
                  1. 创建两个隐藏的 iframe(在 css 样式中添加“display: none;”)。让您的第二个 iframe 指向您自己域中的内容。

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

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

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

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

                  【讨论】:

                    【解决方案15】:

                    检查http://taiyolab.com/mbtweet/scripts/twitterapi_call.js 中的post_method 函数——上述iframe 方法的一个很好的例子。

                    【讨论】:

                      【解决方案16】:

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

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

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

                      【讨论】:

                      • 没想到使用 CNAME 重定向。好决定!我还没有尝试过,但我假设 CNAME 会欺骗浏览器认为它正在与同一个站点进行交互?我将使用它来发布到 Amazon S3,所以我希望它可以工作。
                      • 我看不出这将如何解决任何问题。跨到不同的子域与跨到不同的域有相同的问题。
                      【解决方案17】:
                      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 的样式,使其隐藏并绝对定位。不确定浏览器是否允许跨站点发布,但如果允许,请这样做。

                      【讨论】:

                      • 其实这有点不准确,因为 ifr.appendChild(frm);不管用。 iframe 是对窗口对象的引用,并且它不存在 appendChild 方法。您需要先在 iframe 中获取文档节点。这需要功能检测才能跨浏览器工作。
                      • 问题! iframe 中收到的响应位于不同的域中,因此主窗口无法访问它,iframe 也无法访问主窗口。所以这个解决方案似乎只适合做 POST,但你不能在之后解析响应:(
                      • 尝试在响应的正文标记中设置一个 onload 到一个 JavaScript 函数,该函数使用响应字符串调用父级中的函数。
                      • 这个答案对我不起作用;我在下面发布了我自己的变体。
                      猜你喜欢
                      • 2014-11-27
                      • 1970-01-01
                      • 2011-04-05
                      • 2012-12-04
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-01-05
                      相关资源
                      最近更新 更多