剛一開始學習JavaScript的時候還不知道ajax跨域到底是什麼,不過經常跨域聽到一些大佬們在聊ajax跨域的問題,想必也有同學和我一樣的,那我們今天就來看看ajax跨域到底是什麼,解決ajax跨域的方法又是哪些!
前言
關於跨域,有N種類型,本文只專注於ajax請求跨域(,ajax跨域只是屬於瀏覽器"同源策略"中的一部分,其它的還有Cookie跨域iframe跨域,LocalStorage跨域等這裡不做介紹),內容大概如下:
1. 什麼是ajax跨域
#原則
表現(整理了一些遇到的問題以及解決方案)
2.如何解決ajax跨域
##JSONP方式
CORS方式
代理請求方式
3. 如何分析ajax跨域
http抓取的分析
一些範例
#什麼是ajax跨域
ajax跨域的原理
#ajax出現請求跨域錯誤問題,主要原因就是因為瀏覽器的「同源策略」,可以參考
CORS請求原理
CORS是一個W3C標準,全名為"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨來源伺服器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。 基本上目前所有的瀏覽器都實現了CORS標準,其實目前幾乎所有的瀏覽器ajax請求都是基於CORS機制的,只不過可能平時前端開發人員並不關心而已(所以說其實現在CORS解決方案主要是考慮後台該如何實現的問題)。 關於CORS,強烈建議閱讀
如何判斷是否為簡單請求?
瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。只要同時滿足以下兩大條件,就屬於簡單請求。1、請求方法是以下三種方法之一:HEAD,GET,POST
2、HTTP的頭資訊不超出以下幾個欄位:
1。 Accept 2。 Accept-Language 3。 Content-Language 4。 Last-Event-ID 5。 Content-Type(只限於三個值application/x-www-form-urlencoded、 multipart/form-data、text/plain)#凡是不同時滿足上面兩個條件,就屬於非簡單請求。
ajax跨域的表現
說實話,當初整理過一篇文章,然後作為了一個解決方案,但是後來發現仍然有很多人還是不會。無奈只能耗時又耗力的調試。然而就算是我來分析,也只會根據對應的表現來判斷是否為跨域,因此這一點是很重要的。 ajax請求時,如果存在跨域現象,並且沒有進行解決,會有如下表現:(注意,是ajax請求,請不要說為什麼http請求可以,而ajax不行,因為ajax是伴隨著跨域的,所以只是http請求ok是不行的)注意:具體的後端跨域設定請看題綱位置。#
第一種現象:No 'Access-Control-Allow-Origin' header is present on the requested resource,並且The response had HTTP status code 404
出現這種情況的原因如下:
1、本次ajax請求是「非簡單請求」,所以請求前會發送一次預檢請求(OPTIONS)
2、伺服器端後台介面沒有允許OPTIONS請求,導致無法找到對應介面位址
解決方案:後端允許options請求
第二種現象:No 'Access-Control-Allow-Origin' header is present on the requested resource,並且The response had HTTP status code 405
#這種現象和第一種有區別,這種情況下,後台方法允許OPTIONS請求,但是一些
配置文件中(如安全配置),阻止了OPTIONS請求,才會導致這個現象
解決方案: 後端關閉對應的安全配置第三種現象:
No 'Access-Control-Allow-Origin' header is present on the requested resource,並且
status 200
這種現象和第一種和第二種有區別,這種情況下,伺服器端後台允許OPTIONS請求,並且接口也允許OPTIONS請求,但是頭部匹配時出現不匹配現象
解決方案:後端增加對應的頭部支持
第四個現象:heade contains multiple values '*,*'
表現現像是,後台回應的http頭部訊息有兩個Access-Control-Allow-Origin:*說實話,這種問題出現的主因是要進行跨領域配置的人不了解原理,導致了重複配置,如: 1、常見於.net後台(一般在web.config中配置了一次origin,然後程式碼中又手動新增了一次origin(例如程式碼手動設定了返回*)) 2、常見於.net後台(在IIS和專案的webconfig中同時設定Origin:*)
#解決方案(一一對應):
1、建議移除程式碼中手動新增的*,只使用專案設定中的即可
2 、建議刪除IIS下的配置*,只用專案配置中的即可如何解決ajax跨域
一般ajax跨域解決就是透過JSONP解決或CORS解決,如以下:(注意,現在已經幾乎不會再使用JSONP了,所以JSONP了解下即可)
JSONP方式解決跨域問題jsonp解決跨域問題是一個比較古老的方案(實際中不推薦使用),這裡做簡單介紹(實際項目中如果要使用JSONP,一般會使用JQ等對JSONP進行了封裝的類別庫來進行ajax請求)
##########實作原理############JSONP之所以能夠用來解決跨域方案,主要是因為<script> 腳本擁有跨域能力,而JSONP正是利用這一點來實現。具體原理如圖#############<p style="max-width:90%"Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "WenQuanYi Micro Hei", "Microsoft Yahei", sans-serif; font-weight: 500; line-height: 1.2; color: rgb(51, 51, 51); margin: 1.5em 0px; font-size: 1.25em; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">實作流程<p>JSONP的實作步驟大致如下(參考了來源中的文章)<p>1、客戶端網頁網頁透過新增一個<script>元素,向伺服器請求JSON數據,這種做法不受同源政策限制<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">function addScriptTag(src) { var script = document.createElement(&#39;script&#39;); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function () { addScriptTag(&#39;http://example.com/ip?callback=foo&#39;); } function foo(data) { console.log(&#39;response data: &#39; + JSON.stringify(data)); };</pre><div class="contentsignin">登入後複製</div></div><p>請求時,接口地址是作為構建出的腳本標籤的src的,這樣,當腳本標籤構建出來時,最終的src是接口返回的內容</p><p>2、服務端對應的介面在回傳參數外新增函數包裹層</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">foo({ "test": "testData" });</pre><div class="contentsignin">登入後複製</div></div><p>3、由於<script>元素請求的腳本,直接作為程式碼執行。這時,只要瀏覽器定義了foo函數,函數就會立即呼叫。作為參數的JSON資料被視為<a href="http://www.php.cn/js/js-jsref-tutorial.html" target="_blank">JavaScript物件</a>,而不是字串,因此避免了使用JSON.parse的步驟。 </p><p>注意,一般的JSONP介面和普通介面回傳資料是有區別的,所以介面如果要做JSONO相容,需要進行判斷是否有對應callback關鍵字參數,如果有則是JSONP請求,傳回JSONP資料,否則傳回普通資料</p><p><strong>使用注意</strong></p><p>基於JSONP的實作原理,所以JSONP只能是「GET」請求,不能進行較為複雜的POST和其它請求,所以遇到那種情況,就得參考下面的CORS解決跨域了(所以如今它也基本被淘汰了)</p><p><span style="font-size: 18px;"><strong>CORS解決跨域問題</strong></span></p><p>CORS的原理上文中已經介紹了,這裡主要介紹的是,實際專案中,後端應該如何配置以解決問題(因為大量專案實踐都是由後端進行解決的),這裡整理了一些常見的後端解決方案:</p><p><strong><span style="font-size: 16px;">PHP後台配置</span></strong></p><p>PHP後台得配置幾乎是所有後台中最簡單的,遵循以下步驟即可:</p><p>第一步:配置Php 後台允許跨域</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><?php header(&#39;Access-Control-Allow-Origin: *&#39;); header(&#39;Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept&#39;); //主要为跨域CORS配置的两大基本信息,Origin和headers</pre><div class="contentsignin">登入後複製</div></div><p>第二步:配置Apache web伺服器跨域(httpd.conf中)</p><p>原始程式碼</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><Directory /> AllowOverride none Require all denied </Directory></pre><div class="contentsignin">登入後複製</div></div><p>改為以下程式碼</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><Directory /> Options FollowSymLinks AllowOverride none Order deny,allow Allow from all </Directory></pre><div class="contentsignin">登入後複製</div></div><p><span style="font-size: 18px;"><strong>Node.js後台設定(express框架)</strong></span></p><p>Node .js的後台也相對來說比較簡單就可以進行設定。只要用express如下設定:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">app.all(&#39;*&#39;, function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); res.header("X-Powered-By", &#39; 3.2.1&#39;) //这段仅仅为了方便返回json而已 res.header("Content-Type", "application/json;charset=utf-8"); if(req.method == &#39;OPTIONS&#39;) { //让options请求快速返回 res.sendStatus(200); } else { next(); } });</pre><div class="contentsignin">登入後複製</div></div><p><span style="font-size: 18px;"><strong>JAVA後台設定</strong></span></p><p>JAVA後台設定只需要遵循以下步驟即可:</p><p>#第一步:取得依賴jar套件</p><p>下載 <a href="https://dailc.github.io/staticResource/blog/basicKnowledge/ajax/resource/cors-filter-2.4.jar" target="_blank">cors-filter-1.7.jar</a>, <a href="https://dailc.github.io/staticResource/blog/basicKnowledge/ajax/resource/java-property-utils-1.9.1.jar" target="_blank">java-property-utils-1.9.jar</a> 這兩個函式庫檔案放到lib目錄下。 (放到對應項目的webcontent/WEB-INF/lib/下)</p><p>第二步:如果專案用了Maven建置的,請新增如下依賴到pom.xml中:(非maven請忽略) </p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>cors-filter</artifactId> <version>[ version ]</version> </dependency></pre><div class="contentsignin">登入後複製</div></div><p>其中版本應該是最新的穩定版本,CORS<a href="http://www.php.cn/code/120.html" target="_blank">過濾器</a></p><p>#第三步:新增CORS配置到專案的Web.xml中( App/WEB- INF/web.xml)</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><!-- 跨域配置--> <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></pre><div class="contentsignin">登入後複製</div></div><p>請注意,以上設定檔請放到web.xml的前面,作為第一個filter存在(可以有多個filter的)</p><p>第四步:可能的安全模組配置錯誤(注意,某些框架中-譬如公司私人框架,有安全模組的,有時候這些安全模組配置會影響跨域配置,這時候可以先嘗試關閉它們)</p><p><span style="font-size: 18px;"><strong>JAVA Spring Boot配置</strong></span></p><p>20171230補充</p><p>#僅列舉簡單的全域配置</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">@Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); // 可以自行筛选 corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); return new CorsFilter(source); } }</pre><div class="contentsignin">登入後複製</div></div><p>新配置,然後新增Configuration註解即可配置成功</p><p><em>PS:這一部分方法是收錄的,沒有親身實踐過,但根據回饋,理論上可行</em></p><p><span style="font-size: 16px;"><strong>NET後台設定</strong></span></p><p>.NET後台設定可以參考以下步驟:</p><p>#第一步:網站設定</p><p>開啟控制台,選擇管理工具,選擇iis;右鍵點選自己的網站,選擇瀏覽;開啟網站所在目錄,用記事本開啟web.config檔加入下述設定資訊,重啟網站</p><p><img src="https://img.php.cn//upload/image/967/666/309/1520488619480166.png" title="1520488619480166.png" alt="解決ajax跨域的最全方案"/></p><p>请注意,以上截图较老,如果配置仍然出问题,可以考虑增加更多的headers允许,比如:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">"Access-Control-Allow-Headers":"X-Requested-With,Content-Type,Accept,Origin"</pre><div class="contentsignin">登入後複製</div></div><p>第二步:其它更多配置,如果第一步进行了后,仍然有跨域问题,可能是:</p><p>1、接口中有限制死一些<a href="http://www.php.cn/php/php-tp-requesttype.html" target="_blank">请求类型</a>(比如写死了POST等),这时候请去除限 制</p><p>2、接口中,重复配置了Origin:*,请去除即可</p><p>3、IIS服务器中,重复配置了Origin:*,请去除即可</p><p><span style="max-width:90%"><strong>代理请求方式解决接口跨域问题</strong></span></p><p>注意,由于接口代理是有代价的,所以这个仅是开发过程中进行的。</p><p>与前面的方法不同,前面CORS是后端解决,而这个主要是前端对接口进行代理,也就是:</p><p>1、前端ajax请求的是本地接口</p><p>2、本地接口接收到请求后向实际的接口请求数据,然后再将信息返回给前端</p><p>3、一般用node.js即可代理</p><p>关于如何实现代理,这里就不重点描述了,方法和多,也不难,基本都是基于node.js的。</p><p>搜索关键字node.js,代理请求即可找到一大票的方案。</p><p><span style="font-size: 16px;"><strong>OPTIONS预检的优化</strong></span></p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">Access-Control-Max-Age:</pre><div class="contentsignin">登入後複製</div></div><p>这个头部加上后,可以缓存此次请求的秒数。</p><p>在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据。</p><p>非常有用,可以大幅优化请求次数</p><p><span style="font-size: 18px;"><strong>如何分析ajax跨域</strong></span><br/></p><p>上述已经介绍了跨域的原理以及如何解决,但实际过程中,发现仍然有很多人对照着类似的文档无法解决跨域问题,主要体现在,前端人员不知道什么时候是跨域问题造成的,什么时候不是,因此这里稍微介绍下如何分析一个请求是否跨域:</p><p><strong><span style="font-size: 16px;">抓包请求数据</span></strong></p><p>第一步当然是得知道我们的ajax请求发送了什么数据,接收了什么,做到这一步并不难,也不需要fiddler等工具,仅基于Chrome即可</p><p>1、Chrome浏览器打开对应发生ajax的页面,F12打开Dev Tools</p><p>2、发送ajax请求</p><p>3、右侧面板->NetWork->XHR,然后找到刚才的ajax请求,点进去</p><p><strong>示例一(正常的ajax请求)</strong></p><p><img src="https://img.php.cn//upload/image/175/917/510/1520488988399650.png" title="1520488988399650.png" alt="解決ajax跨域的最全方案"/></p><p>上述请求是一个正确的请求,为了方便,我把每一个头域的意思都表明了,我们可以清晰的看到,接口返回的响应头域中,包括了</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">Access-Control-Allow-Headers: X-Requested-With,Content-Type,Accept Access-Control-Allow-Methods: Get,Post,Put,OPTIONS Access-Control-Allow-Origin: *</pre><div class="contentsignin">登入後複製</div></div><p>所以浏览器接收到响应时,判断的是正确的请求,自然不会报错,成功的拿到了响应数据。<p><span style="font-size: 14px;"><strong>示例二(跨域错误的ajax请求)<p>为了方便,我们仍然拿上面的错误表现示例举例。<p><img src="https://img.php.cn//upload/image/388/553/921/1520489032985324.png" title="1520489032985324.png" alt="解決ajax跨域的最全方案"/><br/><p>这个请求中,接口Allow里面没有包括OPTIONS,所以请求出现了跨域、<p><img src="https://img.php.cn//upload/image/887/649/402/1520489054612338.png" title="1520489054612338.png" alt="解決ajax跨域的最全方案"/><p>这个请求中,Access-Control-Allow-Origin: *出现了两次,导致了跨域配置没有正确配置,出现了错误。<p>更多跨域错误基本都是类似的,就是以上三样没有满足(Headers,Allow,Origin),这里不再一一赘述。<p><strong>示例三(与跨域无关的ajax请求)<p>当然,也并不是所有的ajax请求错误都与跨域有关,所以请不要混淆,比如以下:<p><img src="https://img.php.cn//upload/image/366/255/916/1520489095569031.png" title="1520489095569031.png" alt="解決ajax跨域的最全方案"/><p><strong>更多<p>基本上都是这样去分析一个ajax请求,通过Chrome就可以知道了发送了什么数据,收到了什么数据,然后再一一比对就知道问题何在了。<p><strong>总结<br/><p>跨域是一个老生常谈的话题,网上也有大量跨域的资料,并且有不少精品(比如阮一峰前辈的),但是身为一个前端人员不应该浅尝而止,故而才有了本文。<p><br/></script>以上是解決ajax跨域的最全方案的詳細內容。更多資訊請關注PHP中文網其他相關文章!