AJAX跨域完全講解
今天在慕課網上學習了AJAX跨域完全講解:www.imooc.com/learn/947
我在收集AJAX面試題的時候其實就已經有過AJAX跨域的問題的了,當時候知道了為什麼會存在跨域,以及跨域解決的方案有哪些,今天隨著課程的學習,又加深了AJAX跨域的理解,以此記錄下來。
為什麼會發生產生跨域問題?
上面的圖也很清晰了,因為瀏覽器為了安全(同源),本身就限制了。
- 當我們傳送XMLHttpRequest請求的時候,如果請求的是別的域(主機域名、埠)不同時,那麼就會產生跨域問題(客戶端無法獲取服務端返回的資料)
值得注意的是:跨域的問題是發生在XMLHttpRequest請求的,也就是說,不是XMLHttpRequest請求是不會有跨域問題的
- 舉個很簡單的例子:在編寫網頁的時候,
<img = src = www.xxxx.xxxx/ >
,URL不是本域的還是可以正常獲取該圖片的
解決跨域問題的思路
明顯地,跨域的問題是由於瀏覽器限制的,是XMLHttpRequest才會發生的,那麼我們可以以這個思路去找找解決思路:
對於瀏覽器的問題,可以使用相關的引數進行啟動瀏覽器,是可以解決跨域的問題,但是通用性是極低的,瞭解即可。
JSONP解決跨域
JSONP是JSON使用的一種補充方式,不是官方的協議。JSONP是一種解決跨域問題的一種協議
JSONP這種解決方案其實現在已經很少用了(複雜一點,需要修改後臺程式碼),但我們可以適當瞭解一下。
使用步驟
在後端增加一個控制器,繼承AbstractJsonpResponseBodyAdvice類,完整程式碼如下:
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
// TODO Auto-generated constructor stub
super("callback2");
}
}
複製程式碼
前端ajax請求:
// 伺服器返回的結果
var result;
$.ajax({
url: base +"/get1",
dataType: "jsonp",
jsonp: "callback2",
//是否需要快取,如果這裡沒有配置快取,那麼請求的URL還會有一個引數
cache:true,
success: function(json){
result = json;
}
});
複製程式碼
注意的是,前端AJAX的jsonp: "callback2",
要和我們的Controllersuper("callback2");
是一致的,不然是不會有效的。
JSONP原理是動態建立script來進行請求的:
JSONP的弊端:
- 要對伺服器的程式碼進行改動
- 只支援GET方法(原理是動態建立script來進行請求的)
- 傳送的不是XMLHttpRequest請求(XMLHttpRequest請求有很多好用的特性)
參考資料:
CORS解決跨域問題
CORS解決跨域問題(也就是我們服務端被呼叫方解決跨域的思路)
對於CORS是怎麼理解的,我就直接摘抄一下:segmentfault.com/a/119000001…的了。
在Java中,我們寫下面這個過濾器,就可以完全解決跨域的問題了:
package com.imooc;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.buf.StringUtils;
public class CrosFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
//帶cookie的時候,origin必須是全匹配,不能使用*
String origin = req.getHeader("Origin");
if (!org.springframework.util.StringUtils.isEmpty(origin)) {
res.addHeader("Access-Control-Allow-Origin", origin);
}
res.addHeader("Access-Control-Allow-Methods", "*");
// 支援所有自定義頭和預檢命令(非簡單請求會有預檢命令)
String headers = req.getHeader("Access-Control-Request-Headers");
if (!org.springframework.util.StringUtils.isEmpty(headers)) {
res.addHeader("Access-Control-Allow-Headers", headers);
}
res.addHeader("Access-Control-Max-Age", "3600");
// enable cookie
res.addHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
複製程式碼
上面提到了非簡單請求,那什麼是非簡單請求呢,可以看下面的圖:
非簡單請求會發出一個預檢命令的(當然了,我們上面的Filter已經解決預檢命令的問題了):
Spring框架解決
如果使用的是Spring框架的話,那就只需要一個註解就能夠解決跨域的問題了:@CrossOrigin
HTTP伺服器層
我們在的商用開發中,一般請求的過程是這樣的:瀏覽器->HTTP伺服器(Nginx,Apache)->應用伺服器(Tomcat,Weblogic)
上面編寫的Filter、Spring框架都是在應用伺服器上解決的,我們也是可以通過HTTP伺服器(Nginx,Apache)來進行解決跨域問題的!
Nginx我用過,Apache我倒是還沒用過,下面就簡單記錄了Nginx和Apache是如何配置的:
Nginx配置:
Apache配置:
代理解決跨域問題
在之前的圖我們已經看到了,解決跨域的問題可以在“呼叫方”中來進行解決。
“呼叫方”解決跨域的問題是這個思路的:讓傳送出去的請求代理成是本域的
舉個例子:
www.zhongfucheng.top是呼叫方
www.zhongfucheng.site是被呼叫方
複製程式碼
它倆是不同域的,但我們可以在nginx或Apache上進行配置代理:將被呼叫方www.zhongfucheng.site對映成別的路徑
比如,像下面的圖,將8080埠的對映成了ajaxServer,當呼叫方訪問ajaxServer路徑時,這樣的方法在外部看起來就不像是跨域了,像是訪問本地(8081埠),但實際訪問別的域(8080埠)
總結
令我感到最簡單的是通過Spring的註解就可以解決跨域的問題了,JSONP的方式已經是很少用的了,因為存在一定的弊端,但瞭解一下也無妨,畢竟可能面試的時候會問到。當沒有用任何框架的時候,寫Filter也不麻煩,也只是配置了一下HTTP頭資訊而已。如果使用Nginx、Apache時,也可以用代理或者配置HTTP頭資訊都可以解決。看完之後,有沒有覺得跨域問題就迎刃而解了。
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y