由于浏览器的同源策略(相同协议、域名、端口),导致跨域无法接口请求和DOM查询。
解决跨域方案:
一、JSONP
原理:利用含有src属性标签:<script>、<img>、<ifram>
,这种获取外部资源没有跨域限制,通过url回调来实现跨域。
ajax与jsonp的区别:ajax是通过XMLHttpRequest获取非本页内容,而jsonp是动态添加script标签调用服务器提供的js脚本。
jsonp的缺点:只支持GET请求;无法保证跨域的安全;难以确定是否请求成功。
JSONP由两部分组成:回调函数和数据,回调函数是当响应到来时应该在页面调用的函数,在请求url中指定好的,数据就是传入回调函数中的JSON数据。格式如下:
url:http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler
响应数据:
flightHandler({
"code": "CA1998",
"price": 1780,
"tickets": 5
});
代码实现
<!DOCTYPE html">
<head>
<title></title>
<script type="text/javascript">
// 得到航班信息查询结果后的回调函数
var flightHandler = function(data){
alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
};
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
var script = document.createElement('script'); // 创建script标签,设置其属性
script.src = url;
document.body.appendChild(script); // 把script标签加入body,此时调用开始
</script>
</head>
<body>
</body>
</html>
二、CORS(跨域资源共享)
引用 :https://www.cnblogs.com/knowledgesea/p/6808411.html
原理:使用额外的HTTP头来告诉浏览器,让运行在一个域上的web应用被准许访问来自不同源服务器上指定的资源。需要前后端同时支持。浏览器会自动添加附加的头信息,服务端需要添加Access-Control-Allow-Origin为*或指定源地址。
实现过程:浏览器将CORS分为简单请求和非简单请求。
简单请求:
(1) 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
(2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
跨域简单请求时,浏览器自动添加Origin字段,说明请求哪个源(协议+域名+端口),服务器根据这个值,决定是否同意这次请求。响应头添加Access-Control-Allow-Origin字段,指定域名或 * 。
如果服务端需要发送Cookie,需要服务端指定Access-Control-Allow-Credentials字段为true,同时Access-Control-Allow-Origin字段必须指定域名,前端AJAX请求需要设置withCredentials为true。如下:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
非简单请求:不满足以上条件的请求。
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求OPTIONS,称为"预检"请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com //指定域名
Access-Control-Request-Method: PUT //请求方法
Access-Control-Request-Headers: X-Custom-Header // 额外发送的头信息字段
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
预检请求回应:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com //指定域名
Access-Control-Allow-Methods: GET, POST, PUT // 支持的方法
Access-Control-Allow-Headers: X-Custom-Header 接受的头信息字段
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
三、Nginx反向代理
原理:Nginx 所代表的角色叫做负载均衡服务器或者反向代理服务器,所有请求首先到达 Nginx 上,再由 Nginx 根据提前配置好的转发规则,将客户端发来的请求转发到某一个 服务端 上去。在上线部署时配置。
正向代理:中间服务器代理客户端,
反向代理:中间服务器代理服务端。
在conf/nginx.conf中 http server 中添加如下配置:
location /api/ {
proxy_next_upstream http_500 http_502 http_503 http_504 error timeout invalid_header;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8001; #机器ip
expires 0;
}
location ^~/SaaSPortal {
alias /export/App/epp_saas_m_portal/SaaSPortal;
try_files $uri $uri/ /SaaSPortal/index.html;
}
四、开发环境webpack配置proxy代理跨域请求
开发环境跨域:由于前后端分离开发,前端通过nodejs来运行,需要一个单独的接口,后端通过Tomcat来运行,也需要接口,从而导致跨域。开发完毕后生成静态文件夹拷贝到后端项目就不存在跨域问题。
vue.config.js 请求转发配置:
proxyTable: {
'/api': {
target:'http://api.douban.com/v2', // 你请求的第三方接口
changeOrigin:true,
// 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,
//这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite:{ // 路径重写,
'^/api': ''
// 替换target中的请求地址,也就是说以后你在请求http://api.douban.com/v2/XXXXX
//这个地址的时候直接写成/api即可。
}
}
}
module.exports = {
devServer: {
host: 'localhost',
port: 8080,
proxy: proxyTable
}
}
axios的baseURL配置:
import axios from 'axios'
Vue.prototype.$axios = axios
axios.defaults.baseURL = '/api' //关键代码
使用例子:
axios.get("/movie/top250").then((res) => {
res = res.data
if (res.errno === ERR_OK) {
this.themeList=res.data;
}
}).catch((error) => {
console.warn(error)
})
原理:
因为我们通过axios默认配置给url加上了前缀/api,我们访问/movie/top250就当于访问了:localhost:8080/api/movie/top250(其中localhost:8080是默认的IP和端口)。
在vue.config.js中的proxyTable中拦截了/api,并把/api及其前面的所有替换成了target中的内容,因此实际访问Url是http://api.douban.com/v2/movie/top250。
至此,纯前端配置代理解决axios跨域得到解决