跨域那些事

最近在作一个音乐webapp的时候,遇到这样一个需求:提取歌曲图片的主题色,而后应用到全局
一开始的思路是把图片绘入到canvas中利用getImageData()获取图片的像素数据,分析这些数据得出最接近图片的颜色。接着问题来了,若是在canvas绘入跨域资源,canvas将受到污染,没法调用方法(由于数据都是在QQ音乐官网抓取的)。说到这,咱们就说说前端跨域的那些事。
对项目感兴趣的点这里👉项目地址前端

什么是跨域

通常来讲,当一个请求url的协议、域名、端口三者之间任意一个与当前页面地址不一样即为跨域。最多见的就是在一个域名下的网页中,调用另外一个域名中的资源。
当浏览器报这样的错的时候,就是跨域请求出问题了
java

为啥要跨域

主要是为了安全,浏览器采用同源策略,对js进行限制,防止恶意用户获取非法数据,同时还防止了大部分XSS攻击(就是向用户界面注入js脚本)。
浏览器的两种同源策略会形成跨域问题:web

  • DOM同源策略。禁止对不一样源的页面的DOM进行操做,主要包括iframe、canvas之类的。不一样源的iframe禁止数据交互的,含有不一样源数据的canvas会受到污染而没法进行操做。
  • XmlHttpRequest同源策略。简单来讲就禁止不一样源的AJAX请求,主要用来防止CSRF攻击。

跨域的时候浏览器为啥会报错

这是由于W3C推出的了一个标准----"跨域资源共享"(Cross-origin resource sharing),简称CORS。该标准定义了跨域访问资源时服务器和浏览器怎么通讯。通俗讲就是浏览器在发现跨域请求的时候会附加一些头信息和服务器进行沟通,来肯定跨域请求通不经过。如今除IE10如下的浏览器都支持这个标准
浏览器会把跨域请求分红两类:简单和非简单请求。express

简单请求

简单请求有如下特征:npm

  1. 请求方法是如下之一
    • GET
    • POST
    • HEAD
  2. 头信息是如下字段之一
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type //该字段类型不能是application/json

同时知足以上两点的就是简单请求,其余就是非简单请求了。
当浏览器把跨域请求识别为简单请求的时候,就会在头信息里附加上一个Origin字段,该字段会把此次请求的来源(协议、域名、端口)带给服务器,服务器就会检查这个请求的来源。
要是服务器赞成了这个来源呢,在正常回复浏览器的同时,就也附加上几条字段做为回礼:json

  • Access-Control-Allow-Origin // 这条写着服务赞成的来源,或者一个表明全部来源的 “*”
  • Access-Control-Allow-Credentials // 这条写着浏览器能够发Cookie过来了
    总的来讲获得服务器的承认了,这样浏览器就能正常收到回应了

要是不一样意,服务器就正常返回数据,啥也不附加,浏览器见不到Access-Control-Allow-Origin会不高兴的,而后就不给你返回的数据了,再而后就是报错,这个错就是上面那这样的(就是提取颜主题色的时候😡)。 并且状态码仍是各类各样的,甚至有多是200😫canvas

非简单请求

这种不简单的请求,好比PUT或DELETE请求,还有 Content-Type字段类型为
application/json的。浏览器会严格一点,在发跨域请求前,会发个“预检”请求看看服务器的态度先,这个预检请求比较特殊,请求方式叫OPTIONS,头信息里不光有Origin字段还有这俩:后端

  • Access-Control-Request-Method // 这条是告诉服务器等会的跨域请求是啥方式
  • Access-Control-Request-Headers // 这条是浏览器跨域请求的时候要额外附加的信息

服务器收到预检请求提交过来的信息后,也会严格一点,不只检查来源,还检查请求方式和头信息字段。
要是服务器赞成了,就在正常的HTTP回应中附加上Access-Control-Allow-Origin字段,也一样写着服务赞成的来源。
这就表明这拿到服务器的承认了,毕竟是经历过严格检查的,接下来的每次跨域请求都会正常进行。
要是不一样意,服务器也是啥都不附加地正常回应,这个时候浏览器看不见Access-Control-Allow-Origin但是会生气的,连跨域请求都懒得发,直接报错。(这种状况还没碰到,就不上图啦)。
因此说报错是浏览器搞得鬼。api

如何解决跨域问题

平常开发中会常常碰到跨域的问题,咱们来看看常见的解决方法:跨域

1.JSONP

像img、script等标签是没有跨域限制的,因而乎程序猿们就想到一个办法,动态建立script标签,经过src属性来进行跨域请求的来源

function fun(data) {
    console.log(data);
};
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.type = 'text/javasctipt';
script.src = 'http://example.com?jsonp=cb';
body.appendChild(script);复制代码

返回的js脚本会直接执行,这样咱们想要的数据就传了进来。
这种方法全部浏览器都兼容,前端能够很轻松的作到跨域请求,但也有一些缺点:

  • 只能经过GET方式请求,一方面是参数长度有限制,二是安全性比较差;
  • 后端须要知道前端的cb是什么样的结构,主要在参数和回调名;
  • 后端须要进行参数和cb的拼接而后才能执行;

本文的需求是要把图片绘入到canvas里,这个方法就行不通了,看下一种

2.服务器代理

服务器不像浏览器那样有跨域限制,可让服务器去请求跨域资源而后再返回给客户端,就拿canvas操做跨域图片来讲,客户端把跨域的url传给服务器,请求到图片后再传回客户端,就能够解决开头说到的那个问题了(上代码啦~)
图片是以二进制流的方式在http协议中传输,因此必定要注意编码格式,不然就返回一堆不知道是啥的东西啦
首先在后台起一个express服务,这里的get请求用的是npm中的https包

apiRoutes.get('/image', function (req, res) {
    const Url = (req.query)['0'];
    https.get(Url, function (response) {
        response.setEncoding('binary');  //二进制binary
        var type = response.headers["content-type"];
        let Data = '';
        response.on('data', function (data) {    //加载到内存
            Data += data;
        }).on('end', function () {          //加载完
            res.writeHead(200, { 'Access-Control-Allow-Origin': '*', "Content-Type": type });   //设置头,容许跨域
            res.end(new Buffer(Data, 'binary'));
        })
    })
});
app.use('/api', apiRoutes)复制代码

而后把跨域的图片url提交到这个路由上,就能够‘假装’成同源图片啦

http://example.com/api/image?0=(跨域的图片url)复制代码

—————————————— 更新 11.17 ————————————————

关于主题色的提取在这里👉主题色提取

以上即是此次的文章分享了,欢迎留言相互学习~

相关文章
相关标签/搜索