爲什麼postman調接口不會跨域而瀏覽器會?
都在說跨域,爲什麼postman能訪問接口,而瀏覽器就不行呢?這裏需要理解什麼是跨域,跨域是指的當前資源訪問其他資源時發起的http請求由於安全原因(由於同源策略,域名、協議。端口中只要有一個不同就不同源),瀏覽器限制了這些請求的正常訪問,特別需要注意的是這些發生在瀏覽器中。而通過postman等工具調用接口時,只是簡單的訪問一個資源,並不存在資源的相互訪問。
知識預熱
回調函數
這裏就不細講回調函數啦,相信各位都能理解,用一個案例來說一下。
“服務器”和“客戶端”初次相識,相談甚歡,客戶端希望服務器把一個叫json的東西給它,”服務器“:你先忙着,我弄好了發給你,把你電話號碼(回調函數)給我。
跨域中的預檢請求即是指瀏覽器在真正發送請求前,會先發送一個Options請求嗅探,請求成功後纔會發送真實的請求。問答三連:
爲什麼需要發送預檢請求?是因爲觸發了瀏覽器的安全校驗。
爲什麼請求會觸發安全校驗?因爲當前請求是一個"複雜請求"。
爲什麼我的請求是“複雜的”?見下面解釋
複雜和簡單請求
簡單請求:請求方法是GET/HEAD/POST,並且contentType爲text/plain、application/x-www-form-urlencoded、multipart/form-data。
不滿足上述條件的視爲複雜請求,開發中我們常觸發這個條件大多因爲我們的請求的contentType設置的是application/json導致的。
簡單請求如果設置了Authentication認證header也會讓請求“升級”爲複雜請求。
理解spring提供的@CrossOrigin註解
@CrossOrigin(maxAge = 3600,origins = "*")
@CrossOrigin可以放在某個方法上,或者放在類上,這樣對類中所有請求方法有效,指定能訪問的域集合,即是設置Access-Control-Allow-Origin。
maxAge 屬性指定了預檢請求的緩存時間,單位是秒。對應http的Access-Control-Max-Age屬性
緩存的內容爲:Access-Control-Allow-Methods和Access-Control-Allow-Headers 提供的信息。
理解使用jsonp解決跨域問題
上面簡單介紹了回調函數,jsonp是如何解決跨域的呢?其實就是利用了一個特性,html頁面中引入不同域的js是不會引發跨域的,下面的案例中,我引入了一個在線的query地址,並且在script標籤的src寫上了後端的接口地址,從前端的角度我們可以想象成只是簡單請求一個js文件,js文件的內容是一個函數調用,剛好瀏覽器本地定義了這個函數,所以請求完畢,就會對這個函數發起調用,當然前後端的函數名需要一樣,這就是我們說的回調函數的機制。
前端
<html>
<body>
<head>
<script src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>
</head>
<h3 id="test">init...</h3>
</body>
<script type="text/javascript">
//定義回調函數,相當於上述說的“電話號碼”
function myCallback(res){
alert(JSON.stringify(res));
}
</script>
<script src="http://localhost:9992/cors/infoWithCallBack"></script>
--------------------------------------------------------------------------------
後端:
/**
* 返回 使用回調函數包裹的json
* @return
*/
@RequestMapping("/infoWithCallBack")
public String info(){
JSONObject map = new JSONObject();
map.put("msg","請求成功!");
map.put("code","0000");
map.put("data","獲取到後端數據!");
String str = map.toString();
return "myCallback(" + str + ")";
}
上面的例子在正常返回的json前面包裹了一個函數名,爲的是能讓瀏覽器執行,如果不採用jsonp的方式,而是後端設置Access-Control-Allow-Origin,則請求到的不是一個標準json,所以我們需要改造一下,兼容兩種方式。
<script type="text/javascript">
$(function(){
$.ajax({
type:'POST'
,dataType:"jsonp"
,url:'http://localhost:9992/cors/info3'
,data:{}
,contentType:'application/json;charset=utf-8'
//,jsonp:"callback" //指定回調函數的名字。一般不改這個,默認callback
//,jsonpCallback:"myCallback" //手動執行發送給服務器的callback參數名,否則由jquery自動幫我們生成,類似jQuery17207352806672191685_1544278219377
,success:function(data){
debugger;
alert("ajax: "+JSON.stringify(data));
}
});
}
</script>
--------------------------------------------------------------------------------
/**
* 3.兼容jsonp和普通json請求
* @return
*/
@RequestMapping("/info3")
public String info3(@RequestParam(name="callback",required = false) String callBack){
JSONObject map = new JSONObject();
map.put("msg","請求成功!");
map.put("code","0000");
map.put("data","獲取到後端數據!");
String result = map.toString();
if(!StringUtils.isEmpty(callBack)){
//如果是json請求,則包裹上回調函數
return callBack + "(" + result + ")";
}
return result;
}
其他遇到的問題
Uncaught SyntaxError: Unexpected token :
在解決跨域時,遇到這種錯誤,一般是前端沒有設置回調函數,而後端返回了被回調函數包裹的json.
info?callback=callback:1 Uncaught ReferenceError: callback is not defined
at info?callback=callback:1
回調函數未定義,請求後端接口的script標籤必須在聲明的回調函數之後定義,否則會報錯。
代碼已push到俠夢的github,https://github.com/Hyq0823/Stardust/tree/master/src/main/java/com/cors
本文分享自微信公衆號 - 俠夢的開發筆記(xmdevnote)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。