ajax跨域的原理
ajax出現請求跨域錯誤問題,主要原因就是因為瀏覽器的“同源策略”。
CORS是一個W3C標准,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
基本上目前所有的瀏覽器都實現了CORS標准,其實目前幾乎所有的瀏覽器ajax請求都是基於CORS機制。
瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。只要同時滿足以下兩大條件,就屬於簡單請求。
- 請求方法是以下三種方法之一:HEAD,GET,POST
-
HTTP的頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type(只限於三個值application/x-www-form-urlencoded、 multipart/form-data、text/plain)
凡是不同時滿足上面兩個條件,就屬於非簡單請求
跨域示例:
出現這種情況的原因如下:
- 本次ajax請求是“非簡單請求”,所以請求前會發送一次預檢請求(OPTIONS)
- 服務器端后台接口沒有允許OPTIONS請求,導致無法找到對應接口地址
解決方案: 后端允許options請求。
如何解決ajax跨域
一般ajax跨域解決就是通過JSONP解決或者CORS解決,如以下:(注意,現在已經幾乎不會再使用JSONP了,所以JSONP了解下即可)
JSONP方式解決跨域問題
jsonp解決跨域問題是一個比較古老的方案(實際中不推薦使用),這里做簡單介紹(實際項目中如果要使用JSONP,一般會使用JQ等對JSONP進行了封裝的類庫來進行ajax請求)
實現原理
JSONP之所以能夠用來解決跨域方案,主要是因為 <script> 腳本擁有跨域能力,而JSONP正是利用這一點來實現。具體原理如圖
實現流程
JSONP的實現步驟大致如下(參考了來源中的文章)
-
客戶端網頁網頁通過添加一個<script>元素,向服務器請求JSON數據,這種做法不受同源政策限制
function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function () { addScriptTag('http://example.com/ip?callback=foo'); } function foo(data) { console.log('response data: ' + JSON.stringify(data)); };
請求時,接口地址是作為構建出的腳本標簽的src的,這樣,當腳本標簽構建出來時,最終的src是接口返回的內容
- 服務端對應的接口在返回參數外面添加函數包裹層
foo({
"test": "testData" });
- 由於<script>元素請求的腳本,直接作為代碼運行。這時,只要瀏覽器定義了foo函數,該函數就會立即調用。作為參數的JSON數據被視為JavaScript對象,而不是字符串,因此避免了使用JSON.parse的步驟。
注意,一般的JSONP接口和普通接口返回數據是有區別的,所以接口如果要做JSONO兼容,需要進行判斷是否有對應callback關鍵字參數,如果有則是JSONP請求,返回JSONP數據,否則返回普通數據
使用注意
基於JSONP的實現原理,所以JSONP只能是“GET”請求,不能進行較為復雜的POST和其它請求,所以遇到那種情況,就得參考下面的CORS解決跨域了(所以如今它也基本被淘汰了)
CORS解決跨域問題
CORS的原理上文中已經介紹了,這里主要介紹的是,實際項目中,后端應該如何配置以解決問題(因為大量項目實踐都是由后端進行解決的):
JAVA后台配置
JAVA后台配置只需要遵循如下步驟即可:
- 第一步:獲取依賴jar包
下載 cors-filter-1.7.jar, java-property-utils-1.9.jar 這兩個庫文件放到lib目錄下。(放到對應項目的webcontent/WEB-INF/lib/下)
- 第二步:如果項目用了Maven構建的,請添加如下依賴到pom.xml中:(非maven請忽視)
<dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>cors-filter</artifactId> <version>[ version ]</version> </dependency>
其中版本應該是最新的穩定版本,CORS過濾器
- 第三步:添加CORS配置到項目的Web.xml中( App/WEB-INF/web.xml)
<!-- 跨域配置--> <filter> <!-- The CORS filter with parameters --> <filter-name>CORS</filter-name> <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class> <!-- Note: All parameters are options, if omitted the CORS Filter will fall back to the respective default values. --> <init-param> <param-name>cors.allowGenericHttpRequests</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>cors.allowOrigin</param-name> <param-value>*</param-value> </init-param> <init-param> <param-name>cors.allowSubdomains</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>cors.supportedMethods</param-name> <param-value>GET, HEAD, POST, OPTIONS</param-value> </init-param> <init-param> <param-name>cors.supportedHeaders</param-name> <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value> </init-param> <init-param> <param-name>cors.exposedHeaders</param-name> <!--這里可以添加一些自己的暴露Headers --> <param-value>X-Test-1, X-Test-2</param-value> </init-param> <init-param> <param-name>cors.supportsCredentials</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>cors.maxAge</param-name> <param-value>3600</param-value> </init-param> </filter> <filter-mapping> <!-- CORS Filter mapping --> <filter-name>CORS</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
請注意,以上配置文件請放到web.xml的前面,作為第一個filter存在(可以有多個filter的)
- 第四步:可能的安全模塊配置錯誤(注意,某些框架中-譬如公司私人框架,有安全模塊的,有時候這些安全模塊配置會影響跨域配置,這時候可以先嘗試關閉它們)
轉自:https://segmentfault.com/a/1190000012469713