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

Ido*_*ham 553 javascript ajax cross-domain

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

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

ryn*_*nop 381

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

如果您控制正在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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这允许您的脚本进行跨域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.');
        }
    });
    
    Run Code Online (Sandbox Code Playgroud)

在步骤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.我测试了android,iPad,iPhone
  4. FF <3.6中存在一个相当大的错误,如果服务器返回非400响应代码并且存在响应正文(例如验证错误),则FF 3.6将不会获得响应正文.这是一个巨大的痛苦,因为你不能使用良好的REST实践.看到这里的 bug (它在jQuery下提交,但我的猜测是它的FF错误 - 似乎在FF4中修复).
  5. 始终返回上面的标题,而不仅仅是OPTION请求.FF在POST的响应中需要它.

  • 这是 4 年前最后一次编辑的 - 现在可以在移动浏览器上使用吗? (2认同)

Dan*_*ich 121

如果您控制远程服务器,您应该使用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();
}
Run Code Online (Sandbox Code Playgroud)

谨防!您将无法直接读取POST的响应,因为iframe存在于单独的域中.帧不允许来自不同域的相互通信; 这是同源政策.

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

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

  • 您需要将form.target设置为某个内容,否则浏览器将从您的站点导航到表单操作URL.此外,字符串必须是唯一的; 如果有其他框架或窗口使用相同的名称,表单可以发布到该窗口而不是您的iframe.但它有多独特?可能不是很好.破坏的可能性非常小.*耸肩* (2认同)

Lou*_*nco 47

  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();
Run Code Online (Sandbox Code Playgroud)

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

  • 问题!iframe中收到的响应位于不同的域,因此主窗口无法访问它,iframe也无法访问主窗口.所以这个解决方案似乎只对POST做好,但是之后你无法解析响应:( (19认同)
  • 实际上,这有点不准确,因为ifr.appendChild(frm); 不管用.iframe是对窗口对象的引用,并且appendChild方法不存在.您需要先在iframe中抓取文档节点.这需要功能检测才能跨浏览器工作. (4认同)
  • 尝试在对JavaScript函数的响应的body标记中设置onload,该函数使用响应字符串调用父函数. (2认同)

Ala*_*ier 23

把事情简单化:

  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
        }
    });
Run Code Online (Sandbox Code Playgroud)

  • `crossDomain:true`奇怪地与真正的跨域请求完全无关.如果请求是跨域的,则jquery会自动将其设置为true. (6认同)

小智 16

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

PHP:

header('Access-Control-Allow-Origin: *');
Run Code Online (Sandbox Code Playgroud)

例如,在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;
}
Run Code Online (Sandbox Code Playgroud)

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


nde*_*uma 9

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


小智 6

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

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

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

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

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


Bas*_*ler 6

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

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

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

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


Suj*_*ewa 5

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

假设"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>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在"server.com"上,我们将在我们将要发布的控制器(称为"Home")上执行以下操作:

    [HttpPost]
    public JsonResult Save()
    {
        //Handle the post data...
    
        return Json(
            new
            {
                IsSuccess = true
            });
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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
            }
        });
    
    Run Code Online (Sandbox Code Playgroud)