概述
跨域問題在分佈式應用中經常會遇到,這裏僅僅介紹跨域的三種解決方案,對跨域的概念及原因不做解釋,感興趣的朋友可以網上自行百度。
Nginx代理
這種方式比較簡單,將A應用和B應用都通過一個統一的地址進行轉發,這樣就可以避免跨域問題出現。
server {
listen 80;
server_name www.gameloft9.top;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /manager {
proxy_pass http://127.0.0.1:6108/manager;
proxy_set_header Host $http_host;
}
location /service {
proxy_pass http://127.0.0.1:9200/service;
proxy_set_header Host $http_host;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
}
在上面的配置中,manager和service屬於不同的域(IP雖然一樣,但是端口不一樣),如果manger裏面的js調用service裏面的接口,會報跨域錯誤。通過nginx轉發後,所有的域都映射到www.gameloft9.top了,然後通過/manager,/service分別轉發到各自的應用中,巧妙的避免了跨域問題。如果是純前端項目,可以直接託管到nginx裏面,也可以解決跨域問題,原理一樣。
JSONP
因爲<script>標籤不受跨域限制,因此在此基礎上出現了JSONP的ajax請求方式。通過包裝一個<script>標籤,去請求接口,然後返回數據及回調函數,這樣就達到了跨域的目的。
/**
* 測試JSONP跨域請求
* */
function testJsonP() {
$.ajax({
url: root + "/testjsonp.do",
type: "GET",//jsonp只支持GET請求
data: request,
dataType: "jsonp",
jsonp: "callback",//指定查詢字符串中回調方法key
jsonpCallback: "print",//指定查詢字符串中回調方法value,例如callback=print
success: function (data) {//返回的是回調方法的入參
//alert(data)
}
})
}
JSONP的請求方式,需要設置dataType爲"jsonp",表明這是一個jsonp的請求,jsonp是指定查詢字符串中回調函數的參數的名稱,jsonpCallback指定的是查詢字符串中回調的函數名。一個標準的jsonp的請求如下所示:
http://localhost:8080/demo/testjsonp.do?callback=print&number=2&_=1519870989148
jsonp只支持get請求,所有的參數都會轉化爲查詢字符串,然後再補上回調函數的參數(就是這裏的callback=print),如果jsonpCallback沒有指定,jQuery會自動生成一個隨機的函數名稱,例如:Query17208297070672462461_1491991295320
當然我們不建議這麼做。
後臺處理如下:
/**
* 測試ajax josnp跨域請求
* */
@RequestMapping(value = "/testjsonp.do", method = RequestMethod.GET)
//@ResponseBody
public void test(Model model,String callback,String number, HttpServletRequest request,HttpServletResponse response)throws Exception{
//如果是spring mvc,啓用了@ResponseBody註解,可以直接返回調用語句
//return callback+"("+result.toString()+")";
Integer result = Integer.parseInt(number);
result = result*result;//計算平方值
Writer client = response.getWriter();
client.write(callback+"("+result.toString()+")");
client.flush();
}
後臺接受到參數後,進行業務邏輯調用,並把結果塞進回調函數中,返回即可。
設置Access-Control-Allow-*頭
jsonp的方式雖然簡便,但有個缺點,就是隻支持get請求,對於只支持post的接口就不支持了。通過後臺設置Access-Control-*等header,可以解決跨域問題,而且get,post都支持。
/**
* 測試ajax 設置head跨域請求
* */
@RequestMapping(value = "/testCrossOrigin.do", method = RequestMethod.POST)
@ResponseBody
public String crossOrigin(Model model,String number,HttpServletRequest request,HttpServletResponse response)throws Exception{
//設置response header,允許跨域請求
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Methods","POST");
response.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type");
response.setHeader("Access-Control-Allow-Max-Age","1728000");//單位:秒,這裏是20天
Integer result = Integer.parseInt(number);
result = result*result;
return result.toString();
}
Access-Control-Allow-Origin設置允許跨域的白名單,在白名單裏的跨域請求是允許的。
Access-Control-Allow-Methods設置接受的方法,這裏只接受POST方法。
Access-Control-Allow-Headers設置接受的請求頭,用逗號分隔。
Access-Control-Allow-Max-Age設置預檢的有效期,單位爲秒。發送正式請求前,瀏覽器會預先發送一個預檢請求,如果服務器返回了上述信息,表明是可以跨越請求的,然後纔會正式發送請求。預檢成功後,在有效期內就不用再發送了。
運行截圖
工程地址:https://github.com/gameloft9/cors-demo
參考文章:1-跨域資源共享CORS詳解
2-cors