【实战晋级】理解跨域以及工作中跨域问题的处理 - 1

640?wx_fmt=png


写在前面

相信大部分前端工程师在日常工作中经常使用 xhr 或者 fetch 从后端 api 里取数据然后进行二次处理,随后渲染到页面。

所以这个跨域问题也就屡见不鲜,当然在一些成熟的公司有自己的技术基础和储备这种问题都已经被处理掉一般不会遇到或者配置下就完事了,但并不是所有公司都有这个基础服务,所以这个时候就需要前端工程师自己来进行分析和处理。

所以本文就从这个角度来说下如何解决日常跨域问题,让我们更高效的和后端同学沟通,更快的解决问题完成工作目标。

现在网络上关于跨域的解析文章已经非常多了,再重复的说也很难说出花儿来。所以本文主要从发现问题和解决问题的思路出发,通过实际的代码来帮助大家更具象的理解和处理跨域。

当然跨域的解决方式多种多样,但本文主要说用的最多最灵活的(前端工作量最少)- CORS 。

跨域的理解

理解跨域不需要什么逻辑,跨域是浏览器的一种安全限制(同源策略),不允许脚本对其他域的资源进行直接访问,你能发请求但是浏览器会从中阻拦,要想解除这个限制就需要遵循一定的协议和规范办事(按规矩办事儿)就可以了。

什么情况下产生跨域

只要是 AJAX 要请求地址的端口、协议、域名(包括通过 ip 访问)只要其中一个不同就会产生跨域(你拿不到想要的数据)。

CORS介绍(真香)

CORS是一个 W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

完成 CORS 需要服务器和浏览器进行配合,除了 IE(IE10以下) 浏览器基本上都支持 。

对于前端来说实现 CORS 不需要做太多工作,都是浏览器自动完成的,浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS协议,就可以自由的进行数据交互了。

到这里理论的话就不说了,更详细的请看阮大佬的文章 http://www.ruanyifeng.com/blog/2016/04/cors.html。

下面开始结合代码复现问题和解决问题。

问题以及处理

准备工作

  • 本机使用 node 和 koa2启动一个监听 8100端口的web服务

  • 指定一个接口 /getdata,并且返回 json 数据

  • 使用中间件 koa-static启动静态页面的访问

  • 模拟跨域,使用 localhost:8100访问 ip:8100/getdata接口则产生跨域

  • 静态页面内使用 xhr 对接发起请求

  • node端

/**	
 * 服务入口	
 */	
var http = require('http');	
var koaStatic = require('koa-static');	
var path = require('path');	
var koaBody = require('koa-body');	
var Koa = require('koa2');	
var app = new Koa();	
var port = process.env.PORT || '8100';	

	
//解析请求的数据为对象	
app.use(koaBody());	
//启动静态资源访问	
app.use(koaStatic(	
    path.resolve(__dirname, '../static')	
));	
//跨域处理	
app.use((ctx) => {	
    //指定一个接口和返回数据	
    var path =ctx.path;	
    if(path==='/getdata'){	
        ctx.body=JSON.stringify({	
            code:0,	
            msg:'success',	
            data:[]	
        });	
    }else{	
        ctx.body='welcome';	
    }	
})	
/**	
 * 启动 web 服务	
 */	
var server = http.createServer(app.callback());	
server.listen(port);	
console.log('server start ......   ');

html

<body>	
    <h2>跨域问题复现和解决</h2>	
    <h3>使用 get 或者 post 发送数据 </h3>	
    <h4>要发送的数据 a=1&b=2</h4>	
    <button type="button" id="btn">发送数据</button>	
</body>	
<script>	
function xhrSend() {	
    var xhr = new XMLHttpRequest();   //创建对象	
    xhr.open('POST', 'http://localhost:8100/getdata', true);	
    xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');	
    xhr.send('a=1&b=2');//发送数据	
    xhr.onreadystatechange = function () {	
        console.log('state change', xhr.readyState);	
        if (xhr.readyState == 4) {	
            var obj = JSON.parse(xhr.responseText); //转为 json 格式	
            console.log(obj);	
        }	
    }	
}	
document.getElementById('btn').addEventListener('click',function (e) {	
        xhrSend();	
});	
</script>

接口验证

同源请求时正常拿到数据。

640?wx_fmt=png

640?wx_fmt=png

场景

用 post或者 get 请求接口数据,结果控制台报如下错误。

提示我们产生了跨域,需要设置响应头 Access-Control-Allow-Origin的值,把请求来源的 Origin加进去。

640?wx_fmt=png

报错解决

Origin是什么?

支持 COSR协议的浏览器会自动在请求头内携带这个头发给服务器,其值就是发起请求域的全写(协议+域名+端口)

640?wx_fmt=png

服务端设置'Origin'

ctx.set('Access-Control-Allow-Origin',ctx.headers.origin);

Access-Control-Allow-Origin是什么?

它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

//跨域处理	
app.use((ctx) => {	
    console.log('receive req');	
    //指定一个接口和返回数据	
    var path =ctx.path;	
    if(path==='/getdata'){	
        //服务端通过 ctx.headers.origin 获取请求中的origin	
        + ctx.set('Access-Control-Allow-Origin', ctx.headers.origin);	
        ctx.body=JSON.stringify({	
            code:0,	
            msg:'success',	
            data:[]	
        });	
    }else{	
        ctx.body='welcome';	
    }	
})

问题解决

响应头里多了个这个 ,表示服务器允许这个地址进行数据请求

640?wx_fmt=png

可以正确的得到数据

640?wx_fmt=png

搞清楚个事儿

先回到上面的代码产生跨域的时候,ajax 的请求已经发出去了,而且 status是200,服务端也收到了请求(我故意 log 了一个 receive data),但是浏览器的响应结果却是空的。

640?wx_fmt=png

640?wx_fmt=png

下图中看到,服务端已收到请求

640?wx_fmt=png

推断:出现这种情况应该是浏览器给禁止了,其实浏览器得到了数据,但是因为跨域并没有把数据交给xhr对象。

既然这样那就抓包再验证下

640?wx_fmt=png

从上图可以得到我们的猜测是正确的,还是浏览器做了手脚,所有的数据发送都正常进行了,只是最后关头浏览器插手了(安全机制)。

PS - 简单请求

虽然咱们上面只是处理 POST 的跨域,其实 GET、HEAD 是一样的,另外 Content-Type 为 application/x-www-form-urlencoded,当然也适用于 multipart/form-data、text/plain(文件上传和发送文本),有兴趣的可以自行修改下代码来进行在验证。

符合下列条件的都属于简单请求,上面的解决办法都适用。

640?wx_fmt=png

最后

本小节到这里就介绍完了,简单请求的跨域处理你了解了吗?

但是上面的 node 端代码存在一个问题,不知道大家有没有发现?

发现的请留言哦,在后面的小节我会说明,到时候咱们继续聊~

本节源码已上传github 

https://github.com/Bigerfe/fe-learn-code-guide/tree/master/src/cors-demo/demo1

640?wx_fmt=png

扫码访问

理论知识参考

http://www.ruanyifeng.com/blog/2016/04/cors.html

更多精彩、好玩、有用的前端内容,请关注公众号《前端张大胖》

640?wx_fmt=png

动动小手,让更多人看到
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zz_jesse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值