Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

跨域请求解决方法 #36

Open
cobish opened this issue Nov 26, 2017 · 0 comments
Open

跨域请求解决方法 #36

cobish opened this issue Nov 26, 2017 · 0 comments
Labels

Comments

@cobish
Copy link
Owner

cobish commented Nov 26, 2017

最近项目碰到了跨域请求的问题,项目地址是 a.example.com,而请求的 cgi 地址是 b.example.com。第一次实际处理跨域请求问题,还是学习到不少的东西,查阅了不少,遂趁机整理一番。

CORS

XMLHttpRequest

说到跨域,还是得区分浏览器来说。先讲讲 chrome 等主流浏览器,包括 IE10+。这些浏览器可以使用 CORS 来实现 ajax 跨域请求。使用 jquery ajax 跨域请求时,会发现没有带上 cookies,这时候需要设置 crossDomainwithCredentials :

$.ajaxSetup({
  crossDomain: true,
  xhrFields: {
    withCredentials: true
  }
});

$.ajax({
  type : 'post',
  url : 'http://b.example.com/cgi/xxoo',
  // ...
});

同时需要后端 PHP 配合设置:

<?php
  $url = 'http://a.example.com';
  header('Access-Control-Allow-Origin: ' . $url);
  header('Access-Control-Allow-Credentials:true');
?>

详情参考阮一峰老师的《跨域资源共享 CORS 详解》

XDomainRequest

jquery ajax 实际上就是 XMLHttpRequest 请求,IE8 与 IE9 并不支持,而它们可以使用 XDomainRequest 来实现。具体实现可参考 StackOverflow 上的两个问答:

jquery 插件

嫌麻烦了,正好有 jquey 插件 jQuery-ajaxTransport-XDomainRequest 帮忙实现了,不过它有几点需要注意的:

  • 只能是 GET 或者是 POST 请求;
  • POST 请求的数据类型必须是 text/plain;
  1. 只支持 HTTP 和 HTTPS 协议。

但是!但是!但是!作者都说了,它不能带上 cookies 呀,那就完全不能用了啊......

qq20171126-113803

jsonp

jsonp 的原理是通过添加一个 script 标签,然后向服务器请求 JSON 数据。服务器收到请求后,将数据放到一个指定的回调函数传回来。

优点:兼容所有老式浏览器。
缺点:只支持 GET 请求,同时需要后端 PHP 支持数据格式传回。

jquery 代码如下:

$.ajax({
  type : 'post',
  url : 'http://b.example.com/cgi/xxoo',
  dataType : 'jsonp',
  jsonp: 'callback', //传递给请求处理程序或页面的,用以获得 jsonp 回调函数名的参数名(默认为: callback)
  jsonpCallback:'success_jsonpCallback', //自定义的 jsonp 回调函数名称,默认为 jQuery 自动生成的随机函数名
  success: function(json) {
    alert('success');
  },
  error: function() {
    alert('fail');
  }
});

PHP 代码如下:

<?php
 $data = '.......';
 $callback = $_GET['callback'];
 echo $callback . '(' . json_encode($data) . ')';
 exit;
?>

form + iframe 提交

有一些请求还是得使用 POST 请求,怎么办......

那就不能用 ajax 请求了,得用老式方法 form 表单提交,因为它能在提交时会带上 cookies

但是 form 表单提交会刷新整个页面,这就尴尬了。还好可以设置 form 的 target 属性到一个空的 iframe,表示在 iframe 里面刷新页面,然后把 iframe 隐藏起来,看起来就像是页面异步请求了。

还有需将页面和 iframe 设置成同一个大域,即:

document.domain = 'example.com';

如果是两个完全不同的域,可以使用 window.name 或 window.postMessage 来实现,主要是要让页面能拿到 iframe 中的返回数据。

form 需将 target 指向 iframe,来实现本页面不会刷新的效果。 html 代码如下:

<form id="myForm" method="post" target="myFrame">
  <!-- .... -->
</form>
<iframe name="myFrame" style="display:none;"></iframe>

然后接口是 form 表单提交,这里需要全局添加一个 callback 方法。可以把 callback 名字传给后端让它回调,这里跟后端 统一后先写死。js 代码如下:

var $form = $('#myForm');

$form.attr('action', 'http://b.example.com/cig/xxoo');
$form.submit();

window.callback = function(json) {
  console.log(json);
  window.callback = null;
}

后端需设置 document.domian,然后 $callback 与前端统一好,最后返回一段 script 标签。php 代码如下:

<?php
  header('Content-type:text/html;charset-utf-8');

  $callback = 'callback';
  $args = array(
    'name' => 'cobish',
    'age' => 24
  );

  $params = json_encode($args);

  echo '<script>document.domain = 'example.com';parent.' . $callback . '(' . $params . ')' . '</script>';
?>

总结

跨域请求接口得视浏览器兼容而定,如果只需兼容主流浏览器,那么可以使用 CORS。如果需兼容 IE8 或 IE9,则 GET 请求可以使用 JSONP,POST 请求可以使用 form + iframe。

三种方法都需要后端的支持。

  • CORS 需后端设置 Access-Control-Allow-OriginAccess-Control-Allow-Credentials 来允许前端访问;
  • JSON 则需后端返回执行的 js 回调函数;
  • form + iframe 则需后端返回一段 script 便签,里面包含 document.domian 设置和执行回调函数。

跨域的解决方案参考《前端跨域问题及解决方案》

@cobish cobish added the 前端 label Nov 26, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant