最近在做一個前后端分離的項目食铐,前端是基于Node.js尉咕,后端是用的springboot框架,本地調(diào)試的時候璃岳,前端發(fā)起ajax請求年缎,會出現(xiàn)跨域的問題,為了解決這個問題铃慷,參考網(wǎng)上的已有方案单芜,將自己的解決方法記錄一下。
- 在項目中添加一個過濾器犁柜,過濾前端發(fā)起的請求洲鸠,通過設置響應的響應頭信息,來達到跨域的目的。而前端不需要做任何處理就可以實現(xiàn)跨域訪問扒腕。
1.實現(xiàn)Filter
接口绢淀,重寫doFilter
方法
import org.apache.http.HttpStatus;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 允許跨域訪問過濾器
*/
public class CorsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
//指定允許其他域名訪問
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
//響應頭設置
httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
//響應類型
httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
if ("OPTIONS".equals(httpServletRequest.getMethod())) {
httpServletResponse.setStatus(HttpStatus.SC_NO_CONTENT);
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 其中
httpServletResponse.setStatus(HttpStatus.SC_NO_CONTENT);
這段代碼是設置響應的狀態(tài)碼,HttpStatus.SC_NO_CONTENT
這個常量代表以下意思:
狀態(tài)碼 | 狀態(tài)碼英文名稱 | 中文描述 |
---|---|---|
204 | No Content | 無內(nèi)容瘾腰。服務器成功處理皆的,但未返回內(nèi)容。在未更新網(wǎng)頁的情況下蹋盆,可確保瀏覽器繼續(xù)顯示當前文檔 |
2.添加相應的配置類
import com.maxus.portal.api.controller.common.CorsFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean CorsFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
//注入過濾器
registrationBean.setFilter(new CorsFilter());
//過濾器名稱
registrationBean.setName("CorsFilter");
//攔截規(guī)則
registrationBean.addUrlPatterns("/*");
//過濾器順序
registrationBean.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
return registrationBean;
}
}
3.重啟你的springboot费薄,前端再次發(fā)起訪問,就沒有了跨域問題了栖雾。至此楞抡,問題就得到了解決,把這個記錄一下析藕,希望能幫到其他人召廷。如果你想了解其中的原理,請繼續(xù)往下看账胧。
1. Access-Control-Allow-Origin
- 服務器默認是不被允許跨域的柱恤。配置
Access-Control-Allow-Origin *
后,表示服務器可以接受所有的請求源(Origin),即接受所有跨域的請求找爱。
2. Access-Control-Allow-Headers 是為了防止出現(xiàn)以下錯誤:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
- 這個錯誤表示當前請求
Content-Type
的值不被支持。其實是我們發(fā)起了application/json
的類型請求導致的泡孩。這里涉及到一個概念:預檢請求(preflight request),請看下面"預檢請求"的介紹车摄。
3. Access-Control-Allow-Methods 是為了防止出現(xiàn)以下錯誤:
Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
- 發(fā)送"預檢請求"時,需要用到方法
OPTIONS
,所以服務器需要允許該方法仑鸥。
預檢請求(preflight request)
- 上面的配置涉及到了一個W3C標準:CROS,全稱是跨域資源共享 (Cross-origin resource sharing)吮播,它的提出就是為了解決跨域請求的。
跨域資源共享(CORS)標準新增了一組 HTTP 首部字段眼俊,允許服務器聲明哪些源站有權限訪問哪些資源意狠。另外,規(guī)范要求疮胖,對那些可能對服務器數(shù)據(jù)產(chǎn)生副作用的HTTP 請求方法(特別是 GET 以外的 HTTP 請求环戈,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發(fā)起一個預檢請求(preflight request)澎灸,從而獲知服務端是否允許該跨域請求院塞。服務器確認允許之后,才發(fā)起實際的 HTTP 請求性昭。在預檢請求的返回中拦止,服務器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數(shù)據(jù))。
- 其實
Content-Type
字段的類型為application/json
的請求就是上面所說的搭配某些 MIME 類型的POST
請求,CORS規(guī)定汹族,Content-Type
不屬于以下MIME類型的萧求,都應該發(fā)起"預檢"請求:
application/x-www-form-urlencoded
multipart/form-data
text/plain
- 所以
application/json
的請求會在正式通信之前,增加一次"預檢"請求顶瞒,這次"預檢"請求會帶上頭部信息Access-Control-Request-Headers: Content-Type
例如:
OPTIONS /api/test HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
... 省略了一些
- 服務器響應時夸政,返回的頭部信息如果不包含
Access-Control-Request-Headers: Content-Type
則表示不接受非默認的的Content-Type。即出現(xiàn)以下錯誤:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
文中如有錯誤搁拙,歡迎指正秒梳。
參考鏈接