🥜 前情提要

了解AJAX是什麼後我們來嘗試實作看看會遇到什麼問題。

XMLHttpRequest:

上回解釋了AJAX的簡介,那這次我們來實作,根據MDN上的說明,可以使用以下程式碼來操作XMLHttpRequest。

MDN 是由瀏覽器四巨頭的Mozilla創建的JavaScript API文檔網站

  1. 網站上說明,需要使用該方法進行初始化
  2. 可以使用open這個函數來初始化請求(就是你要互動的行為是什麼)
  3. open函數,有2個必填參數,1個可有可無的參數
  4. method傳入String變數對應的功能:
  5. 傳送我的互動要求
  6. 查詢我的結果

綜上所述,用程式碼來寫應該是這樣:

1
2
3
4
5
var xhr = new XMLHttpRequest(); // 初始化物件
// 初始化指令,告知我需要與 resource/config.json 這個路徑進行獲取的互動,並且採用異步方式。
xhr.open("GET" , "resource/config.json" , false)
xhr.send(); // 傳送指令
console.log(xhr.response) // 把結果印出來

可以從圖中看出,已經正確發出請求了

  1. xhr 是說明使用 XMLHttpRequest 的請求方式
  2. status 是狀態碼
  • 200 - 請求成功
  • 301 - 資源(網頁等)被永久轉移到其它URL
  • 404 - 請求的資源(網頁等)不存在
  • 500 - 內部伺服器錯誤

緩存問題:

今天如果我跟Server請求一張圖片叫做A,Server丟給你後瀏覽器會自動幫你把圖快取下來,然後隔了10分鐘我又請求一次了,但是這時後瀏覽器認為我又要A這張圖,就會把我的請求擋住,並把快取裡面的A圖給你,這樣能夠有效節省請求消耗,這是一種優化效能手段。

但是也會產生,如果我要的A圖在這10分鐘被換掉,但是網頁機制還是認為我又要同張圖,把我請求擋掉,於是我就拿不到新的A圖,這問題就是緩存、快取問題。

為了解決這問題,我們可以採用以下任一種方案解決:

  1. 新增時間標籤
  2. 新增雜湊檢查碼

時間戳:

假設圖片叫做A,我們請求的檔案改為 A?v=20190203 代表2月3日拿到的資料,而2月4日拿到的新圖片則取名為A?v=20190204

這樣系統就會認定這兩個東西是完全不同的東西,就不會使用快取就不會有問題。

雜湊檢查碼

可以使用一些數學公式,將檔案命名為 A_XXXXXXX,這個XXX是雜湊檢查碼,雜湊的詳細原理這裡不講述,簡單來說他是一個數學公式,它的特色就是:

任何輸入,經過雜湊函數後,會產生獨一無二的數據,該數據無法逆推輸入是什麼,該數據是獨一無二,輸入微小的改動,輸出會有劇烈的改變,並且也只有這個輸入能產生這個數據,類似指紋的概念。

而我們將圖片的數據傳入雜湊函數,獲得獨一無二的該圖片的「數字指紋」,這個XXXX就是他的指紋,所以只要圖片改動,檔名就會不一樣,就可以避開緩存機制。


PS: 緩存機制有HttpHeader決定,但是瀏覽器為了自身效能,所以真實緩存機制是由瀏覽器決定,那怕Header說不緩存,瀏覽器也有權決定要不要緩存。

跨域問題:

何謂跨域,自從人們發明AJAX後發現一些安全性的問題,於是對AJAX設定了一個同源策略,跨域就是當處於非同源時發起的請求,這種行為叫做跨域,首先我們先了解什麼是同源:

1
2
3
4
5
6
7
假設目前使用者在:http://store.company.com:

http://store.company.com/dir2/other.html 同源
http://store.company.com/dir/inner/another.html 同源
https://store.company.com/secure.html 不同源 協定不同
http://store.company.com:81/dir/etc.html 不同源 埠號不同
http://news.company.com/dir/other.html 不同源 主機位置不同

以上Case定義了什麼情況算是同源,什麼情況算是非同源,而如果你身在https://example.com 網址發起https://google.com 的請求資料,很明顯為非同源,於是就會產生跨域問題,瀏覽器為了安全性將這個動作視為失敗處理,也就是請求Error。

為什麼跨域這會是個問題?

設想這樣一種情況:使用者開啟了A網站,A網站是一家網路銀行,使用者登入以後,又去瀏覽駭客網站(A片網站之類的)。

而駭客網站裡的程式碼,程式碼內記載了所有網路銀行的轉帳API,於是你進入網站後自動執行轉帳API,而因為你有登入過,所以有Cokie存在,於是瀏覽器就自動幫你把轉帳API附上Cookie,這樣網路銀行的Server收到封包後發現你有Cookie於是就認為是你本人真的要轉帳,轉帳成功。

為了避免這問題,於是有了同源政策來保護使用者,但仍有一些駭客發現同源政策的漏洞進行攻擊:CSRF、XSS、JSON Hijacking等等攻擊。

值得注意的是,跨域請求雖然會被瀏覽器擋下來,但攔截的是回應(Response),不是請求(Request),請求指定的內容仍然會傳送到對方Server,開發者要特別注意這點!

但如果要部份解決跨域問題還是可以的,以下提出三種方案:CORS、代理伺服器、JSONP

CORS:

最標準、正確的解決方法是透過 W3C 規範 的「跨來源資源共用(Cross-Origin Resource Sharing,CORS)」,透過 伺服器在 HTTP Header 的設定,讓瀏覽器能取得不同來源的資源。
CORS 規範中,清楚定義了跨域存取控制的運作方式。

全球資訊網協會(W3C),提出了CORS該規範紀載著

  1. 瀏覽器發出的任何請求,一率都會在Header中記載,是誰發的(也就是你的網址)
  2. 瀏覽器收到的任何請求,Header中的某個欄位可以設定,只要你的網址跟那欄位不一樣,一率當作失敗請求。

收到請求的Header欄位就叫做Access-Control-Allow-Origin
而上述這規則是W3C制定,於是製作瀏覽器的廠商都必須遵守這些規則。

1
2
Acess-Control-Allow-Origin: * // 任何人發起請求都可以無視同源規則。
Access-Control-Allow-Origin: http://store.company.com // 只有身處這個網址的人可以無視同源規則。

注意!!
只有W3C是規則制定人,瀏覽器廠商是實做人,但是但是 無良的瀏覽器廠商不會遵守這規則,所以來路不明的瀏覽器勿用

代理伺服器:

回憶一下,跨域請求為什麼會失敗嗎,因為W3C制定的規範,於是瀏覽器開發商就會遵守這規則,也就是「只有」瀏覽器會限制跨域,那麼我不用瀏覽器發請求,而是用代理伺服器呢,這樣就必定不會受到跨域影響。

代理伺服器有很多方案,自己架或是使用別人架好的伺服器並且提供API給你,此處我們使用別人架好的當作示範:

首先我要拿的資料是源自於:https://github.com/
而我選擇別人架好的代理伺服器是:CORS-ANYWHER

根據CORS-ANYWHER的使用說明,只要將https://cors-anywhere.herokuapp.com/ 後面加入你要解析的URL就可以獲取非跨域的資料,像是https://cors-anywhere.herokuapp.com/https://github.com/。

而CORS-ANYWHER 設定所有人都可以跨域獲取這個網頁的資源,所以我們可以在任何地點發起請求這個代理伺服器API服務。

之後我們再次使用xhr發起請求:

1
2
3
4
var xhr = new XMLHttpRequest(); // 初始化物件
xhr.open("GET" , "https://cors-anywhere.herokuapp.com/https://github.com/" , false)
xhr.send(); // 傳送指令
console.log(xhr.response) // 把結果印出來

這樣雖然能解決跨域問題,但以此為延伸

  1. 效率低下,因為你必須由第三方轉達請求和轉收封包。
  2. 因為是由代理伺服器發起,所以你自身其他網站(銀行網站)的Cookie並不會被獲取,畢竟Cookie也是W3C規定瀏覽器才有