由上報數據延伸的Ajax跨域問題

如何使用img標籤上報數據

var REPORT_URL = '//gamepre.ltyun.cc/api/public/page-data/save?'	//數據上報接口
var img = new Image;						//建立img標籤
img.onload = img.onerror = function(){		//img加載完成或加載src失敗時的處理
    img = null;								//img置空,不會循環觸發onload/onerror
};
img.src = REPORT_URL + Build._format(params); //數據上報接口地址拼接上報參數做爲img的src
複製代碼
img標籤之onError事件與問題

有時,img標籤中的src圖片加載失敗,原來的圖片位置會出現一個碎片圖標,用戶體驗會降低。javascript

要想去除載入圖片失敗時顯示在左上角的碎片圖標,就要借用img標籤的onerror事件和javascript,img標籤支持onerror 事件,在裝載文檔或圖像的過程當中若是發生了錯誤,就會觸發onerror事件。可使用一張提示錯誤的圖片代替載入失敗的圖片。代碼以下:php

<img src="images/bg.png" onerror="javascript:this.src='images/bgError.png';">
複製代碼

當src默認的src不存在時,就會觸發onerror事件,在該事件中指定img.src。html

但問題來了,若是 bgError.png也不存在,則繼續觸發 onerror事件,致使循環,故出現錯誤。前端

解決方法:java

第一種:去掉 onerror 代碼;或者更改 onerror 代碼爲其它;或者確保 onerror 中的圖片足夠小,而且存在.ajax

第二種:img=img.onLoad=img.onError=nulljson

使用img標籤上報數據

不少地方不使用Ajax發送請求上報數據而使用img標籤,主要有如下兩個緣由:api

  1. 不存在Ajax跨域的問題,可作不一樣源的請求;
  2. 很古老的標籤。沒有瀏覽器的兼容問題,及時老瀏覽器不支持部分js也支持img。

由於img不存在跨域的問題,因此不少網站也作了圖片的防盜鏈處理,好比百度、知乎是有處理的,博客園應該是沒有相應處理的。跨域

跨域

跨域,指的是瀏覽器對於javascript的同源策略的限制,是瀏覽器施加的安全限制。所謂同源是指協議,域名,端口均相同瀏覽器

http://www.123.com/index.html 調用 http://www.123.com/server.php (非跨域)

http://www.123.com/index.html 調用 https://www.123.com/server.php (協議不一樣,跨域)

http://www.123.com/index.html 調用 http://www.456.com/server.php( 主域名不一樣,跨域)

http://abc.123.com/index.html 調用 http://def.123.com/server.php (子域名不一樣,跨域)

http://localhost:8080/index.html 調用 http://localhost:8081/server.php (端口不一樣,跨域)

http://localhost:8080/index.html 調用 http://127.0.0.1:8081/server.php (域名不一樣,跨域)

請注意:localhost和127.0.0.1雖然都指向本機,但也屬於跨域。

能夠跨域的標籤及使用場景

容許跨域加載資源的有三個標籤。分別是:

  1. img標籤 用於打點統計,統計網站多是其餘域
  2. link標籤 可使用CDN的資源
  3. script標籤 可使用CDN資源、用於JSONP

Ajax跨域解決方案

瀏覽器同源策略有兩種限制,其限制之一是不能經過ajax的方法去請求不一樣源中的文檔。 第二個限制是瀏覽器中不一樣域的框架之間不能進行js的交互操做的,咱們主要解決第一種限制Ajax跨域。

1. JSONP 只支持get請求

實現以下:

<script type="text/javascript">
    var messagetow = function(data){
        //data 經過跨域獲得的信息
        console.log(data);
    };
    var url = "gamepre.ltyun.cc/api/public/page-data/save?callback=messagetow";
    var script = document.createElement('script');
    script.setAttribute('src', url);
    document.getElementsByTagName('head')[0].appendChild(script);
</script>
複製代碼

2. CROS跨域資源共享

它容許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。CORS須要瀏覽器和服務器同時支持。目前,全部瀏覽器都支持該功能,IE瀏覽器不能低於IE10。

整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於前端開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。

所以,實現CORS通訊的關鍵是服務器。只要服務器實現了CORS接口,就能夠跨源通訊。

-CORS請求類型-

跨域請求有兩種形式:

一、 簡單請求 二、 非簡單請求

簡單請求知足如下條件:

HTTP請求方法(區分大小寫)爲如下之一:

· HEAD · GET · POST

HTTP頭部匹配(不區分大寫小)爲如下:

· Accept

· Accept-Language

· Content-Language

· Last-Event-ID

· Content-Type: 只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

簡單請求

對於簡單請求,瀏覽器直接發出CORS請求。具體來講,就是自動在頭信息之中,增長一個Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
複製代碼

上面的頭信息中,Origin字段用來講明,本次請求來自哪一個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否贊成此次請求。

若是Origin指定的源,不在許可範圍內,服務器會返回一個正常的HTTP迴應。瀏覽器發現,這個迴應的頭信息沒有包含Access-Control-Allow-Origin字段,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequestonerror回調函數捕獲。注意,這種錯誤沒法經過狀態碼識別,由於HTTP迴應的狀態碼有多是200。

若是Origin指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true		//布爾值,是否容許發送cookie,默認cros中不包含
Access-Control-Expose-Headers: FooBar		//header除默承認獲取字段
Content-Type: text/html; charset=utf-8
複製代碼

withCredentials 屬性

CORS請求默認不發送Cookie和HTTP認證信息。若是要把Cookie發到服務器,一方面要服務器贊成,指定Access-Control-Allow-Credentials:true。另外一方面,開發者必須在AJAX請求中打開withCredentials屬性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
複製代碼
非簡單請求

咱們想使用PUT或者DELETE請求,又或者咱們想使用Content-Type:application/json來支持JSON。那麼,咱們就須要非簡單請求

咱們在使用非簡單請求時,表面上看起來客戶端只發送了一個請求,但實際上,要完成一次非簡單請求,客戶端在私底下是要向服務器發起兩次請求的。第一次請求("預檢"請求),是向服務器確認權限,一旦被受權,則發起第二次請求(真正意義上的數據請求)。且,第一次請求也能夠被緩存,因此不是每次咱們發起非簡單請求,都會預請求一次。

預檢請求的HTTP頭信息:

OPTIONS /cors HTTP/1.1			//請求方法是OPTIONS,表示這個請求是用來詢問的
Origin: http://api.bob.com
Access-Control-Request-Method: PUT	//CORS請求會用到哪些HTTP方法
Access-Control-Request-Headers: X-Custom-Header	//逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive	//告訴WEB服務器或者代理服務器,在完成本次請求的響應後,保持鏈接,等待本次鏈接的後續請求
User-Agent: Mozilla/5.0...
複製代碼

服務器收到"預檢"請求之後,檢查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段之後,確認容許跨源請求,就能夠作出迴應。

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com		//該字段設爲星號時,表示贊成任意跨源請求。
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
複製代碼

若是瀏覽器否認了"預檢"請求,會返回一個正常的HTTP迴應,可是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不一樣意預檢請求,所以觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制檯會打印出以下的報錯信息。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
複製代碼

一旦預檢查獲得受權信息,那麼瀏覽器就會發送真正的跨域請求了。且,請求和服務器響應與簡單CORS請求同樣。

參考跨域資源共享 CORS 詳解,更多詳情點擊查看

相關文章
相關標籤/搜索