跨域概述
为什么会有跨域
跨域解决办法:1、jsonp;2、后台代理
手写实现jsonp跨域(包括服务器端代码)
跨域问题
A服务器上的页面去获取B服务器上的资源,由于XMLHttpRequest同源策略限制,A页面是获取不到数据的。
同源:协议、域名、端口号任何一个都要相同,否则就会出现跨域请求。
为什么会跨域
为了安全
解决办法
方法一:jsonp
原理:使用script标签可以跨域请求资源的特性,去发送请求,得到数据
- 核心思想
动态在页面创建script标签,然后给src设置url,这个url就是跨域请求的链接,然后在页面声明一个方法,这个方法名要和后台返回的数据里带有的名字一样,一般情况下,这个名字是动态设置的,在url中传给后台。 - 代码示例
// localhost:9528 服务器
// index.html
<body>
<script>
function getJsonp(data){
console.log(data)
}
</script>
<script src='http://lcoalhost:9527/getData?call=getJson'></script>
</body>
// localhost:9527 服务器
// json/getData 接口 (基于express框架)
router.get("/getData", function (req, res) {
res.send("getJson({a:1,b:2,c:3})")
});
-
效果图
输出结果 -
总结
当script标签去加载 getData?call=getJson 接口时(就当是去请求一个js文件),接口返回数据后,浏览器会去执行得到的数据,这个时候浏览器发现数据是 getJson({a:1,b:2,c:3}) ,那这个getJSON是一个方法呀,所以他会立即执行这个方法,而这个上面我们已经定义好了,所以控制台就打印除了数据,如上图。
这只是一个简单的实现原理说明,现在有成熟的库可以使用 json ,如果有时间,后期将出关于jsonp框架源码分析的文章。
- jsonp的缺点:只能解决文本类型的数据跨域,如果是图片呢?那就得需要下面这种方式了。
方法二:后台代理
原理:自己搭建一个服务器,提供一个接口,以供前端页面使用,前端页面把url链接发送给后台;然后接到参数后,服务器发送http请求(这儿可以设置各种请求头),接到数据然后返回给页面就行了。
-
使用场景:在之前写的一个页面,图片路径是用的微信公众号发布的图片,但是在我的项目中请求这些图片,就会出现如下图一样的问题。
微信公众号图片防盗链
当然,除了本文要讲的方式之外,还有其他解决办法,比如 iframe 标签,但是图片过多的话,这个方式就不太好了(我的页面里面将全是iframe标签)。我的页面是运行在手机端的,所以还要考虑性能问题,只有把压力交给了服务器。这是效果图,里面图片全是公众号的图片。
效果图 - 代码示例
// 前端代码
<img src="http://host/micro-site/change?url=http://mmbiz.qpic.cn/mmbiz_jpg/ZHRvtsJlIIyCKYib6WAzImVbYEN9ljaUNIDtUMpAAAdkib2lVjPeUgI8GaNudwez1CUW9sHVLTrRoxzAoAPMicCgw/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1">
// 前面 host/micro-site/change 自己服务器的域名和接口
// url链接是图片的路径
// 服务器接到url后,发送http请求,根据url请求数据,然后返回给前端页面。
// 后端代码 java
@GetMapping("change")
public void image(String url, HttpServletResponse response) {
response.setHeader("Content-Type", "image/*");
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Referer", "");
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36");
try {
CloseableHttpResponse execute = httpClient.execute(httpGet);
HttpEntity entity = execute.getEntity();
entity.writeTo(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
- 后台代理缺点:这种方式显得有些笨重,多了一层数据请求,本来发送到一个服务器就可以了,现在要通过两台服务器才能返回数据,速度相对慢了一些。
网友评论