Home / PostsPost
Ajax跨域访问
嘟噜聪2016/07/24 16:37:59
4980人已阅
简介 众所周知道,跨域都一个让前端大湿们很头疼的一个问题。JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。
Ajax跨域访问
众所周知道,跨域都一个让前端大湿们很头疼的一个问题。JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。
什么是跨域
简单来说就是不同网站之间相互访问,比如A网站通过Ajax
获取B网站的内容,这就算是跨请求。请求子域名也算是跨域请求了,解决方法也有很多。
[TOC]
怎么样才算跨域
下表列出请求方式
比如在当前网站: http://lattecake.com/
下一个js发起请求
URL | 说明 | 是否可通信 |
---|---|---|
http://lattecake.com/learn/ | 同一域下不同目录或文件 | 是 |
http://www.lattecake.com/learn/ | 这种情况也算是子域名 | 否 |
http://lattecake.com:8000/learn/ | 同一域下不同端口 | 否 |
https://lattecake.com/learn/ | 同一域下不同协议 | 否 |
http://127.0.0.1/learn/ | 域名所对应的ip | 否 |
http://photo.lattecake.com/learn/ | 子域名 | 否 |
http://dudulu.me/learn/ | 不同域名 | 否 |
如果前后端分离开发的话以上这些情况前端是无能为力的,为了解决这种问题,请往下面看。
如何解决这类问题
通常咱们为了什么ajax跨域有以下几种方法
- 基于
iframe
来实现(讨厌iframe
所以这里不演示) - 使用
jsonp
的方式 - 通过HTML5里的
window.postMessage
进行传输 - 设置Nginx代理来实现跨域请示允许
web sockes
的方式
Iframe
iframe
比较讨厌,说好的不演示还是写了......
lattecake.com/post/20091
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta charset="UTF-8">
<title>post 20091</title>
</head>
<body>
<iframe src="http://photo.lattecake.com/photo/exif/02762910-823-8368-9259-071970843233" id="iframe"></iframe>
<script>
document.domain = 'http://photo.lattecake.com/';
var ifr = document.getElementById("iframe");
var doc = ifr.contentDocument || ifr.contentWindow.document;
console.log(doc);
</script>
</body>
</html>
不建议这么使用
JsonP
如果确定以后确实是布在不同域下的话可以考虑这种方式,这种方式也比较简单,不过我不太喜欢!推荐指数一般
来看jsonP的方式,以jQuery
为例:
jQuery
$(function(){
$.ajax({
type:'get',
url: 'http://photo.lattecake.com/photo/exif/02762910-823-8368-9259-071970843233',
dataType: 'jsonp',
jsonp: "jsoncallback",
success: function(data) {
console.log(data);
},
error : function() {
alert('fail');
}
}, 'json');
})
需要服务器返回
jsoncallback
函数,也就是"jsonp" 参数里定义的那个函数。
Ext JS
Ext.Ajax.cors = true;
Ext.Ajax.useDefaultXhrHeader = false;
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['empid', 'name', 'email']
});
var myStore = Ext.create('Ext.data.Store', {
model: 'User',
autoLoad:true,
proxy: {
type: 'jsonp',
url: 'http://photo.lattecake.com/photo/exif/02762910-823-8368-9259-071970843233',
},
listeners:{
'load':function( store, records, successful, eOpts ){
console.log(records);
}
}
});
window.postMessage
具体我也没用过,大概的访问依然是ifram客户端与客户端之间进行通信
参考: http://www.webhek.com/window-postmessage-api
Nginx CORS 代理
当然我觉得还是这种方式最方便,你原来js该咋写现在还咋写,不用变化,跨域问题交给nginx去处理。
下面是nginx的部伪代码
location ~ ^/(app_dev|config)\.php(/|$) {
if ( $request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'OPTION, POST, GET';
add_header 'Access-Control-Allow-Headers' 'X-Requested-With, Content-Type';
add_header 'Access-Control-Allow-Headers' "Authorization";
add_header 'Content-Length' 0;
add_header 'Content-Type' text/plain;
return 200;
}
fastcgi_index app_dev.php;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
try_files $uri $uri/ app_dev.php /app_dev.php$is_args$args;
include /etc/nginx/fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param APP_ENV dev;
}
如果$request_method
为 OPTIONS 的话,咱们设置一下header 头,并返回状态。
也可以 $http_origin ~ <允许的域(正则匹配)>
进行匹配。
我把它定义在app_dev.php
块中,只在当请求访问lattecake.com/app_dev.php
时才会进这个区间,也就是开发的时候走这个区间,产环境会把前端与后端合并,所以生产环境并不存在跨域问题,所以我觉得这种方式比较好。
Access-Control-Allow-Origin
Access-Control-Allow-Origin
这个是设置你允许哪些域下的来源可以通过请求。
关于OPTIONS
ExtJS
的伪代码
var form = this.getView().getForm('auth-dialog');
if (!form.isValid()) {
Ext.Msg.alert("错误", "请输入用户名密码!");
return;
}
var me = this;
form.submit({
clientValidation: true,
method: 'POST',
url: 'http://redis.lattecake.local/app_dev.php/login_check',
waitMsg: '正在验证...',
type: 'json',
success: function (form, action) {
if (action.response && action.response.responseText) {
var response = Ext.decode(action.response.responseText);
if (response.success) {
me.redirectTo('dashboard', true);
}
}
},
failure: function (form, action) {
var message = "请求错误!";
if (action.response && action.response.responseText) {
var response = Ext.decode(action.response.responseText);
message = response.message;
// window.BaseInfo._csrf_token = response.token;
}
Ext.Msg.alert("错误", message);
Ext.toast('可以打开控制台看看啥错误!');
}
});
一般前端框架比如"ExtJS"、"AngularJS", 框架监测到访问的域名可能存在跨域的话会先发送一个OPTIONS
请求,验证是否可进行通信,如果返回可通信才会真正发起一个POST、GET请求。
下图是框架发起的OPTIONS请求,当如果服务器的Nginx并没有设置允许跨域请示的时候,它会返回一个405状态码。
当我们设置了跨域后重新发起请示,框架会发出三个请求,这个"GET
"是什么鬼?我也不知道,按理来说应该只会发一个POST
才对呀!难道是因为我设置了"'Access-Control-Allow-Methods' 'OPTION, POST, GET';
"吗?
下图是第一次发送的"OPTIONS
"请示所返回的header头信息,状态是200
当接收到200状态后,又发起了一次"GET
"请求,返回状态是302
在GET
完事后发送"POST
"请求,并提交相关数据!
这里需要注意
返回的Response
必须加上Access-Control-Allow-Origin
并设置允许的网站,否则会报以下错误。
PHP伪代码
return new JsonResponse($data, 400, ['Access-Control-Allow-Origin' => '*']);
某些情况下推荐,如果生产环境对指定域名比较信任的话可以考虑用这种方式。
web socket
听说使用 web socket 可以很有效的解决这类问题,具体的我也没实验过,有机会再写吧,先这样。
上面还有忘写的吗?中间被打断过多次...
很赞哦! (1)
文章评论
点击排行
本栏推荐
标签
站点信息
- 微信公众号
