ajax跨域问题

我们先回顾一下域名地址的组成:

http:// www . google : 8080 / script/jquery.js

http:// (协议号)

www  (子域名)

google (主域名)

8080 (端口号)
  • 当协议、子域名、主域名、端口号中任意一各不相同时,都算不同的“域”。

  • 不同的域之间相互请求资源,就叫“跨域”。
    (上面这段不是我写的)

浏览器有一个同源策略,就是说ajax只能请求域名完全一致的页面的资源
完全一致就是说,域名,协议,和端口号都要完全相同,否则是访问不了的。

解决方式1

代理,通过后台服务器去请求资源,得到响应之后,再将数据返回给前端,这种属于后台技术,不过多阐述。

解决方式2

JSONP
我们知道script标签是可以引用其他地方的资源的(如在线引入jquery)
也就是说script标签是不受同源策略的影响的,我们将url写入script的src里面就会发送一个get请求。
但是这样还不够,因为script标签是会把请求得到的数据当作js代码来执行的,而我们大部分情况下请求的并不是可以执行的js代码。

不过,随着这种情况的发展,就发展出来了一种非正式的jsonp协议,就是说我们在请求的时候,最url最后写上回调函数的函数名,那么服务器返回的时候就会将返回的数据作为刚刚我们给服务器的函数的参数,这样一来,我们预先设定好回调函数就可以处理返回的数据了。

那么,下面我们开始实践,思路就是,先写好回调函数,然后利用js动态的在dom树里面添加一个script节点,将我们要请求的url加进去,就可以了,服务器响应返回数据之后,会执行我们预先设定好的回调函数。 下面上代码。

<script>
var vi = document.getElementById('view'), //显示结果
t = document.getElementById('test'), //输入框
t1 = document.getElementById('test1'); //按钮
function jsonpback(js){
var s = js,
    id = s.result,
    id1 = id[0][0],
    id11 = id[0][1],
    id2 = id[1][0],
    id22 = id[1][1],
    id3 = id[2][0],
    id33 = id[2][1],
    id4 = id[3][0],
    id44 = id[3][1],
    id5 = id[4][0],
    id55 = id[4][1];
// console.log(s);
vi.innerHTML = `商品名称:${id1},货号:${id11} <br/>商品名称:${id2},货号:${id22} <br/>
商品名称:${id3},货号:${id33} <br/>商品名称:${id4},货号:${id44} <br/>商品名称:${id5},货号:${id55} <br/>`;
}
function creatScript(src){
var s = document.createElement('script');
s.src = src;
document.body.appendChild(s);
}
function click(){
var a = t.value,
    src = 'https://suggest.taobao.com/sug?code=utf-8&q='+a+'&callback=jsonpback';
creatScript(src);
}
t1.onclick = function(){
click();
}
</script>

这段代码会请求淘宝的一个接口,然后返回前10个商品的商品名和货号(我只显示了5个)

总结一下,JSONP的优点是简单实用,并且兼容版本较低的浏览器,

缺点就是只能发送POST请求。

解决方式3 CORS

CORS需要浏览器和服务器同时支持

CORS全称Cross-Origin Resource Sharing,是HTML5定义的访问跨域资源的规范。
下面说CORS具体是怎么实现的
CORS的请求分两类,简单请求和复杂请求,这里主要说简单请求,其实基本上能满足大部分的需求了。
简单请求的请求方法只能是 POST,HEAD,GET里面的其中一个,并且,不能有自定义的请求头,严格的讲看下面这个图(廖雪峰大神的博客里面截的)

说完请求的分类,下面说下具体流程

对于简单请求,也是我们用的最多的,浏览器发现是跨域请求的时候,会自动的在请求头加一个origin字段,来标识这个请求是从哪来的(其实就是源地址),

如果服务器觉得可以接受,那么就会在响应头上加上Access-Control-Allow-Origin字段,字段里面填写我们发过去的origin里面的值,或者*号(表示接受任意域名的请求),这样一来,浏览器接到响应之后检查响应头,如果发现没有Access-Control-Allow-Origin字段,返回的信息我们就用不了了,就表示跨域失败

如果Access-Control-Allow-Origin字段里面的值是我们的源地址或者*号,那么表示跨域成功

可见,跨域成不成功决定权在服务器手里。

但是,要注意第一句话,要服务器和浏览器同时支持,
但是在实际测试的时候origin的值是null,等我弄明白再填这个坑

这里找了个接受所有类型的接口 算是测试成功了吧

但是,还是觉得差点什么,等我再查查博客吧。

再说一下复杂请求,

复杂请求比简单请求多了一个预检请求(preflight)

就是说,当我们发送请求的时候,浏览器检查发现是复杂请求,就会先给目标服务器发送一个预检请求

服务器会检查这三个字段Origin、Access-Control-Request-Method和Access-Control-Request-Headers,并回应

如果Access-Control-Allow-Origin字段使我们的源地址或者*号,就表示同意跨域请求。如果服务器拒绝,那么响应头就不会有cors相关的字段
至此预检请求完成,如果成功,然后浏览器就会正式的发送请求,以后的每次请求也都和简单请求一样了。

解决方式3

WebSocket协议,这是一种通信协议,我现在还不太了解,但了解一点关于跨域的内容
WebSocket协议的请求头里面会有Origin字段,也是表示请求源,正是因为这个字段,该协议才不受同源策略的影响。
收到请求的时候,服务器会根据这个字段判断此次是否通信,如果该地址在白名单里面,服务器就会作出回应。