为什么会出现跨域问题 —— 同源策略
跨域限制仅仅是浏览器的行为,服务器没有跨域限制。
同时满足以下三个条件才有可能发生跨域问题:
-
浏览器限制
-
跨域
-
XMLHttpRequest 请求
跨域的概念:协议、域名、端口都相同才叫同域,否则都叫跨域
出于安全考虑,服务器不允许ajax跨域获取数据,但是可以跨域获取文件内容。
-
举个简单的例子:在编写网页的时候,
<img src = ‘www.xxxx.xxxx/’ >
URL 不是本域的也可以获取数据 -
当我们发送 XMLHttpRequest 请求的时候,如果请求的是别的域,那么就会产生跨域问题,客户端无法获取服务器端返回的数据
-
跨域的问题是发生在 XMLHttpRequest 请求的,也就是说,如果不是 XMLHttpRequest 请求,是不会产生跨域问题的。
解决跨域的方法
从浏览器出发
使用相关的参数启动浏览器,解决跨域
通用性极低,不推荐
JSONP 解决跨域
什么是 JSONP ?
script 标签中的 src 属性是可以跨域的。jsonp 是浏览器端发送请求之后,服务器端包装好一段 json 数据,并放在一个 callback 函数中,返回一个 js 文件。浏览器端动态引入这个 js 文件,就会调用这个 callback 函数,获取到数据。
原理:
首先在客户端注册一个 callback ,然后把 callback 的名字传给服务器。此时,服务器先生成 json 数据,然后以 javascript 语法的方式,生成 function,function 名字就是传递上来的 callback 名称。最后将 json 数据直接以入参的方式,放置 function 中,这样就生成 js 语法的文档,返回给客户端。客户端浏览器,解析 script变迁,并执行返回 javascript 文档,此时数据作为参数,传入了客户端预先定义好的 callback 函数里。
例子:
跨域服务端提供的js脚本动态生成,这样调用者可以传一个参数过去告诉跨域服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是跨域服务器就可以按照客户端的需求来生成js脚本并响应了。
//跨域服务器
//文件:flightResult.php
flightHandler({
"code":"CA1998",
"price": 1780,
"tickets": 5
});
//本地
<script type="text/javascript">
// 得到航班信息查询结果后的回调函数
var flightHandler = function(data){
alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
};
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var url = "跨域服务器/flightResult.php?code=CA1998&callback=flightHandler";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
</script>
缺点:
-
只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求 – 因为 script 标签,只支持GET
-
只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题。
CORS 解决跨域问题
一、CORS 简介:
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
二、两种请求
浏览器将 CORS 请求分为两类:简单请求和非简单请求
只要同时满足一下两个条件,就属于简单请求,反之为非简单请求
- 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
- HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
三、简单请求
对于简单请求,浏览器直接发出 CORS 请求 :在头部信息中,直接加入一个 Origin 字段
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Origin 字段用来说明本次请求来自哪个源(协议+域名+端口)。服务器根据这个值,决定是否同意这次请求。
- 如果 Origin 指定的源不在许可范围内,服务器会返回一个正常的 http 回应。这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,浏览器就会知道出错了,从而抛出一个错误,被 XMLHttpRequest 的 onerror 回调函数捕获。
注意,这种错误无法通过状态码识别,因为返回的状态码可能是 200 。
- 在许可范围之内,服务器返回的响应,会多出几个头信息的字段
Access-Control-Allow-Origin: http://api.bob.com //请求时 Origin 字段的值或者 * ,表示接受对应域名的请求
Access-Control-Allow-Credentials: true //表示是否允许发送 cookie
Access-Control-Expose-Headers: FooBar //拿到指定字段
Content-Type: text/html; charset=utf-8