現在咱們一起來討論瀏覽器跨域請求數據的相關問題。說這樣可能不是很標準,因為拒絕跨域請求數據並不是瀏覽器所獨有的,之所以會出現跨域請求不了數據,是因為瀏覽器基本都實現了一個叫"同源策略"的安全規範。該規範具體是什麼呢?我們在MDN上找到了一份資料,地址如下:
瀏覽器同源策略講解
總的來說,當A網址和B網址在協議、 端口、 域名方面存在不同時,瀏覽器就會啟動同源策略,拒絕A、B服務器之間進行數據請求。
說了同源策略,紙上得來終覺淺,絕知此事要躬行,到底同源策略是怎麼體現的呢?下面我將結合代碼一步一步進行演示。
1、A服務器請求不了B服務器的情況
既然是跨域,我就假設我有兩個域名,分別是A 和localhost , A 表示小編在阿里雲上主機域名, localhost 顧名思義就是小編的開發機器了。我們想像這樣一個場景,在localhost 上部署一個index.html 文件,在A 服務器上部署一個簡單的spring-boot 後台服務,並提供一個簡單的接口暴露給index.html 文件調用,最後瀏覽器請求localhost 的index.html 文件,看瀏覽器提示什麼?
index.html
<!DOCTYPE html><html><head><title>測試跨域訪問</title><meta charset="utf-8"/></head><body> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function() { $.ajax({ type : "get", async : true, url : "http://A/hello/map/getUser.json",// 請求A服務器上的接口type : "json", success : function(data) { // 打印返回的數據console.log("success,and return data is " + data); } }); }); </script> <h2>hello world</h2></body></html>瀏覽器上請求index.html 文件,顯示如下:
可以發現,請求被瀏覽器給拒絕了,提示我們不允許跨域請求數據,很難受,怎麼解決呢?
2、使用jsonp 解決跨域請求
首先講下原理,jsonp解決跨域問題主要利用了<script> 標籤的可跨域性,也就是src 屬性中的鏈接地址可以跨域訪問的特性,因為我們經常將src 屬性值設置為cdn的地址,已加載相關的js庫。
index.html
<!DOCTYPE html><html><head><title>測試跨域訪問</title><meta charset="utf-8" /></head><body> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function() { $.ajax({ type : "get", async : true, jsonp : "callbackName",// 後端接口參數名jsonpCallback : "callbackFunction", // 回調函數名url : "http://A/hello/map/getUser.json", dataType : "jsonp", // 數據格式為jsonp success : function(data) { console.log("success"); } }); }); </script> <script type="text/javascript"> var callbackFunction = function(data) { alert('接口返回的數據是:' + JSON.stringify(data)); }; </script></body></html>A 服務器上的接口代碼為:
/** * * The class JsonBackController. * * Description:該控制器返回一串簡單的json數據,json數據由一個簡單的User對象組成* * @author: huangjiawei * @since: 2018年6月12日* @version: $Revision$ $Date$ $LastChangedBy$ * */@RestController@RequestMapping(value = "/map")public class JsonBackController { private static final Logger logger = LoggerFactory.getLogger(JsonBackController.class); /** * 解決跨域請求數據* @param response * @param callbackName 前端回調函數名* @return */ @RequestMapping(value = "getUser.json") public void getUser(HttpServletResponse response, @RequestParam String callbackName) { User user = new User("huangjiawei", 22); response.setContentType("text/javascript"); Writer writer = null; try { writer = response.getWriter(); writer.write(callbackName + "("); writer.write(user.toString()); writer.write(");"); } catch (IOException e) { logger.error("jsonp響應寫入失敗! 數據:" + user.toString(), e); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { logger.error("輸出流關閉異常!", e); } writer = null; } } }}後端傳入一個參數callbackName 回調函數名,然後返回一段js代碼給前端,js代碼格式如下:
callbackName + ( data ) + ;
瀏覽器請求localhost 服務器上的index.html 文件,結果如下:
上面這種方式是通過jquery + jsonp 解決跨域問題的,剛剛不是說可以用<script> 標籤的src 屬性嗎?四的。
localhost 服務器上的index.html
<!DOCTYPE html><html><head><title>測試跨域訪問</title><meta charset="utf-8" /></head><body> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script type="text/javascript"> var callbackFunction = function(data) { alert('接口返回的數據是:' + JSON.stringify(data)); }; </script> <script type="text/javascript" src="http://A/hello/map/getUser.json?callbackName=callbackFunction"></script></body></html>瀏覽器顯示效果和上面一致。但此處需要注意的是, src 表示引入一個js文件,由於是直接調用接口,而接口返回的數據又剛好是一段js代碼,故能被執行。另外,第二個<script> 標籤順序不能顛倒,不然會出現callbackFunction 函數找不到的情況。
工程代碼地址: https://github.com/SmallerCoder/jsonpDemo
最後總結下,解決跨域的方案有很多種,jsonp只是其中一種,具體情況需要具體分析。希望此文對你有幫助,謝謝閱讀,歡迎github給顆start ,麼麼噠!也希望大家多多支持武林網。