在上一节,我们介绍了如何进行生产环境打包。一般情况下将打好的生产包和后台程序部署到tomcat,然后启动tomcat即可通过域名端口号访问。那么如果是这种情况,生产包与后台程序不存在跨域问题;如果生产包与后台程序违背了同源策略
[1],那么就会发生跨域问题。还有一种情况必然会发生跨域问题:开发环境中与后台程序进行测试。
跨域限制是服务端的一个行为,当
后端程序开启对某些域名的访问限制后
,只有同域名或者制定域名下的页面可以调用,这样相对来说更安全、图片也可以防盗链。跨域限制一般只在浏览器端存在,对于服务端或iOS、Android等客户端是不存在的。
我们注意到后端程序其实是可以处理跨域问题,使用的方法是CORS
(稍微会后详细文章介绍CORS)。那么如果只通过前端代码或者配置前端的运行环境如何解决跨域问题?
- JSONP
- 使用代理
JSONP是使用了<script></script>标签的特性:可以加载非同源策略的特性实现回调函数处理返回数据。由于我们开发的是SPA应用,在组件内无法直接使用<script>标签;另外如果在index.html文件里使用<script>标签倒是可以,但是一个项目有很多的请求,那每遇到请求就新增一个<script>标签显然是不可取的。
所以本篇着重介绍使用代理解决跨域-----使用基于Node.js的request
库做代理。
1. 安装request
npm install request --save-dev
在根目录下新建proxy.js,写入以下内容:
const request = require('request');
const http = require('http');
// 知乎url
const BaseUrl = 'http://news-at.zhihu.com/api/4';
// 设置代理的域名和端口
const hostname = '127.0.0.1';
const port = '8080';
const imgPort = "8081";
// 创建API代理
const apiServer = http.createServer((req, res) => {
const url = BaseUrl + req.url;
const options = {
url: url
};
function callback(error, response, body) {
if(!error && response.statusCode === 200) {
// 设置编码类型,否则中文会乱码
res.setHeader('Content-type', 'text/plain;charset=UTF-8');
// 设置跨域
res.setHeader('Access-Control-Allow-Origin', '*');
// 返回结果
res.end(body);
}
}
request.get(options, callback);
})
// 监听apiServer
apiServer.listen(port, hostname, () => {
console.log(`接口代理运行在http://${hostname}:${port}`);
})
// 创建imgServer
const imgServer = http.createServer((req, res) => {
console.log(req.url)
const url = req.url.split('/img/')[1];
const options = {
url: url,
encoding: null
}
function callback(error, response, body) {
if(!error && response.statusCode === 200) {
const contentType = response.headers['content-type'];
res.setHeader('Content-type', contentType);
res.setHeader('Access-Control-Allow-Origin', '*');
res.end(body);
}
}
request.get(options, callback);
})
// 监听imgServer
imgServer.listen(imgPort, hostname, () => {
console.log(`图片代理运行在http:${hostname}:${imgPort}`);
})
上述代码实现监听两个端口:8080和8081。8080用于接口代理,8081实现图片代理。比如请求http://news-at.zhihu.com/api/4/news/before/20180627,开发时改写为http://127.0.0.1:8080/news/before/20180627,图片的真实地址是https://pic2.zhimg.com/v2-66bd54ab382ac860cec3822b3f8d7811.jpg开发时改写成http://127.0.0.1:8081/img/v2-66bd54ab382ac860cec3822b3f8d7811.jpg。
运行以下命令:
node proxy.js
如果运行成功,会显示两条运行日志:
对于接口的Ajax请求,前端有很多实现方案,比如jQuery的$.ajax()方法,但是为了使用Ajax这一个功能,引入整个jQuery是不可取的。Vue官方提供vue-resource插件,但是不再维护,而是推荐使用axios。
axios不仅在开发环境会用到,在生产打包后也会用,所以进行全局安装:
npm install axios --save
在components文件夹内新建axios.vue,代码如下:
<template>
<div class="zhihu-list">
<ul class="stories">
<li
class="list"
v-for="(story, idx) in stories"
:key="idx"
>
<span>title: {{story.title}}</span>
<span>id: {{story.id}}</span>
<span>ga_prefix: {{story.ga_prefix}}</span>
<img
v-for="(img,imIdx) in story.images"
:key="imIdx"
:src="imgPath + img"
>
</li>
</ul>
</div>
</template>
<script>
import Util from '../Util.js';
export default {
name: '',
data() {
return {
stories: [],
imgPath: Util.imgPath
};
},
mounted() {
Util.ajax.get('/news/before/20180627').then(res => {
this.stories = res.stories;
})
}
}
</script>
<style scoped>
</style>
在app.vue中引入该组件(同title.vue、count.vue引入方式一样),打开浏览器效果如下:
注意:代理图片可能会由于网络原因导致加载时间过长。
至此,我们实现了如何使用Node.js的request进行代理以解决跨域问题的问题。
完整代码github地址:https://github.com/zhiyuanMain/zhihu-daily
-
域名、协议、端口号至少一个不同即违背同源策略,那么在ajax访问的时候就会出现跨域问题。 ↩