跨域那些事

最近在作一個音樂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 ————————————————

關於主題色的提取在這裏👉主題色提取

以上即是此次的文章分享了,歡迎留言相互學習~

相關文章
相關標籤/搜索