搞定所有的跨域請求問題: jsonp & CORS
網上各種跨域教程,各種實踐,各種問答,除了簡單的 jsonp 以外,很多說 CORS 的都是行不通的,老是缺那麼一兩個關鍵的配置。本文只想解決問題,所有的程式碼經過親自實踐。
本文解決跨域中的 get、post、data、cookie 等這些問題。
本文只會說 get 請求和 post 請求,讀者請把 post 請求理解成除 get 請求外的所有其他請求方式。
JSONP
jsonp 的原理很簡單,利用了【前端請求靜態資源的時候不存在跨域問題】這個思路。
但是 只支援 get,只支援 get,只支援 get。
注意一點,既然這個方法叫 jsonp,後端資料一定要使用 json 資料,不能隨便的搞個字串什麼的,不然你會覺得結果莫名其妙的。
前端 jQuery 寫法
$.ajax({
type: "get",
url: baseUrl + "/jsonp/get",
dataType: "jsonp",
success: function(response) {
$("#response").val(JSON.stringify(response));
}
});
dataType: "jsonp"。除了這個,其他配置和普通的請求是一樣的。
後端 SpringMVC 配置
如果你也使用 SpringMVC,那麼配置一個 jsonp 的 Advice 就可以了,這樣我們寫的每一個 Controller 方法就完全不需要考慮客戶端到底是不是 jsonp 請求了,Spring 會自動做相應的處理。
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice(){
// 這樣如果請求中帶 callback 引數,Spring 就知道這個是 jsonp 的請求了
super("callback");
}
}
以上寫法要求 SpringMVC 版本不低於 3.2,低於 3.2 的我只能說,你們該升級了。
後端非 SpringMVC 配置
以前剛工作的時候,Struts2 還紅遍天,幾年的光景,SpringMVC 就基本統治下來了國內市場。
偷懶一下,這裡貼個虛擬碼吧,在我們的方法返回前端之前調一下 wrap 方法:
public Object wrap(HttpServletRequest request){
String callback = request.getParameter("callback");
if(StringUtils.isBlank(callback)){
return result;
} else {
return callback+"("+JSON.toJSONString(result)+")";
}
}
CORS
Cross-Origin Resource Sharing
畢竟 jsonp 只支援 get 請求,肯定不能滿足我們的所有的請求需要,所以才需要搬出 CORS。
國內的 web 開發者還是比較苦逼的,使用者死不升級瀏覽器,老闆還死要開發者做相容。
CORS 支援以下瀏覽器,目前來看,瀏覽器的問題已經越來越不重要了,連淘寶都不支援 IE7 了~~~
Chrome 3+
Firefox 3.5+
Opera 12+
Safari 4+
Internet Explorer 8+
前端 jQuery 寫法
直接看程式碼吧:
$.ajax({
type: "POST",
url: baseUrl + "/jsonp/post",
dataType: 'json',
crossDomain: true,
xhrFields: {
withCredentials: true
},
data: {
name: "name_from_frontend"
},
success: function (response) {
console.log(response)// 返回的 json 資料
$("#response").val(JSON.stringify(response));
}
});
dataType: "json",這裡是 json,不是 jsonp,不是 jsonp,不是 jsonp。
crossDomain: true,這裡代表使用跨域請求
xhrFields: {withCredentials: true},這樣配置就可以把 cookie 帶過去了,不然我們連 session 都沒法維護,很多人都栽在這裡。當然,如果你沒有這個需求,也就不需要配置這個了。
後端 SpringMVC 配置
對於大部分的 web 專案,一般都會有 mvc 相關的配置類,此類繼承自 WebMvcConfigurerAdapter。如果你也使用 SpringMVC 4.2 以上的版本的話,直接像下面這樣新增這個方法就可以了:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**/*").allowedOrigins("*");
}
}
如果很不幸你的專案中 SpringMVC 版本低於 4.2,那麼需要「曲線救國」一下:
public class CrossDomainFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Origin", "*");// 如果提示 * 不行,請往下看
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
filterChain.doFilter(request, response);
}
}
在 web.xml 中配置下 filter:
<filter>
<filter-name>CrossDomainFilter</filter-name>
<filter-class>com.javadoop.filters.CrossDomainFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CrossDomainFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
有很多專案用 shiro 的,也可以透過配置 shiro 過濾器的方式,這裡就不介紹了。
注意了,我說的是很籠統的配置,對於大部分專案是可以這麼籠統地配置的。文中類似 “*” 這種配置讀者應該都能知道怎麼配。
如果讀者發現瀏覽器提示不能用 ‘*’ 符號,那讀者可以在上面的 filter 中根據 request 物件拿到請求頭中的 referer(request.getHeader("referer")),然後動態地設定 "Access-Control-Allow-Origin":
String referer = request.getHeader("referer");
if (StringUtils.isNotBlank(referer)) {
URL url = new URL(referer);
String origin = url.getProtocol() + "://" + url.getHost();
response.addHeader("Access-Control-Allow-Origin", origin);
} else {
response.addHeader("Access-Control-Allow-Origin", "*");
}
2018-04-28:今天終於知道為什麼有時候會提示我們 * 不支援了,原來是隻要前端寫了 withCredentials: true 那麼瀏覽器就會提示這個,一種辦法就是這裡說的使用動態構造 origin 的方式,另一種辦法就是跨域不傳 cookie,讓前端把 cookie 要傳的資訊(如 sessionId/accessKey) 放到 header 中或者直接寫在 request 的引數裡。
前端非 jQuery 寫法
jQuery 一招鮮吃遍天的日子是徹底不在了,這裡就說說如果不使用 jQuery 的話,怎麼解決 post 跨域的問題。大部分的 js 庫都會提供相應的方案的,大家直接找相應的文件看看就知道怎麼用了。
來一段原生 js 介紹下:
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// 如果有 withCredentials 這個屬性,那麼可以肯定是 XMLHTTPRequest2 物件。看第三個引數
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// 此物件是 IE 用來跨域請求的
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// 如果是這樣,很不幸,瀏覽器不支援 CORS
xhr = null;
}
return xhr;
}
var xhr = createCORSRequest('GET', url);
if (!xhr) {
throw new Error('CORS not supported');
}
其中,Chrome,Firefox,Opera,Safari 這些「程式設計師友好」的瀏覽器使用的是 XMLHTTPRequest2 物件。IE 使用的是 XDomainRequest。
我想,對於 95% 的讀者來說,說到這裡就夠了,我就不往下說了,讀者如果有需要補充的,請在評論區留言。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31555151/viewspace-2218301/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- CORS跨域請求CORS跨域
- php 支援jsonp跨域請求PHPJSON跨域
- 使用cors完成跨域請求處理CORS跨域
- ajax跨域請求之CORS的使用跨域CORS
- 簡單的實現jsonp跨域請求JSON跨域
- CORS跨域問題梳理CORS跨域
- web 跨域請求安全問題Web跨域
- CORS跨域限制以及預請求驗證CORS跨域
- cors解決跨域問題CORS跨域
- options 請求跨域問題處理跨域
- springboot系列文章之實現跨域請求(CORS)Spring Boot跨域CORS
- 深入跨域問題(3) – 利用 JSONP 解決跨域跨域JSON
- 深入跨域問題(3) - 利用 JSONP 解決跨域跨域JSON
- 跨域請求之jQuery的ajax jsonp的使用解惑跨域jQueryJSON
- 深入跨域問題(2) - 利用 CORS 解決跨域跨域CORS
- Nginx解決前端跨域問題 CORS跨域配置Nginx前端跨域CORS
- 前端http請求跨域問題解決前端HTTP跨域
- Koa2框架利用CORS完成跨域ajax請求框架CORS跨域
- Springboot處理CORS跨域請求的三種方法Spring BootCORS跨域
- 學習AJAX必知必會(5)~同源策略、解決跨域問題(JSONP、CORS)跨域JSONCORS
- 中止請求和超時 跨域的HTTP請求 認證方式 JSONP跨域HTTPJSON
- .net webapi 處理前端請求跨域問題WebAPI前端跨域
- 跨域請求中常見的幾個問題跨域
- 關於vue請求laravel介面跨域問題VueLaravel跨域
- 跨域CORS圖片上傳問題跨域CORS
- Cors跨域問題中文官方文件CORS跨域
- 跨域問題,解決方案 – CORS方案跨域CORS
- CORS跨域時,為何會傳送兩次請求?CORS跨域
- js ajax請求封裝及解決node請求跨域問題JS封裝跨域
- 深入跨域問題(1) - 初識 CORS 跨域資源共享跨域CORS
- web前端跨域解決方案JSONP,CORS,NGINX反向代理Web前端跨域JSONCORSNginx
- 跨域請求跨域
- vue請求後端資料和跨域問題Vue後端跨域
- 跨域問題(CORS / Access-Control-Allow-Origin)跨域CORS
- 解決跨域問題 barryvdh/Laravel-cors跨域LaravelCORS
- spring-boot + jsonp 解決前端跨域問題SpringbootJSON前端跨域
- vue跨域請求Vue跨域
- SpringBoot中通過CORS解決跨域問題Spring BootCORS跨域