序言
目前項目開發流行前後端分離,前後分離勢必會出現CORS問題了。CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。本文主要討論前後端分離之後對於Cors問題的解決方案。需要說明的是本文章前端使用Angular9,後端使用的是SpringBoot 2.1.3.RELEASE。
一、場景
前端發起請求主要關注JSON形式和JSONP形式。
1.1 JSON數據請求場景
前端請求代碼實現形式,這裏使用的是HttpClientModule。
```
doPost() {
const url = `http://localhost:8080/interface/testPost`;
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
this.httpClient.post(url,{"username":"張三丰","age":45}, httpOptions).subscribe( (response) => {
console.log(response);
this.masterInfo = response;
});
}
```
後端SpringBoot應用解決Json數據請求的跨域問題有多種:
1、使用@CrossOrigin註解來實現跨域,使用註解可以以最細粒度控制可跨域範圍,此註解可添加在Controller類上和方法上,Spring Framework 4.2開始支持此註解。
2、CORS全局配置一個CorsConfigure類實現WebMvcConfigure接口或者繼承WebMvcConfigurerAdapter類,形式都是一樣,都是給CorsRegistry 設置跨域允許的請求源:
```
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.maxAge(3600)
.allowCredentials(true);
}
```
3、實現Filter接口自定義一個允許跨域的過濾器
```
@WebFilter(urlPatterns = "/*")
public class CorsFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin","*");
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
```
1.2 JSONP數據請求場景
這需要說明一下瀏覽器的特性,瀏覽器不允許頁面中的腳本程序跨域讀取數據,但卻允許HTML引用跨域的資源,如圖片,CSS和腳本程序。對於腳本程序的引用比較特殊,它被瀏覽器解析以後,就和本地的腳本程序別無二致且可立即進行解釋並執行。
前端JSONP請求需要用到的依賴HttpClientJsonpModule。請求實現方式是使用HttpClient發起jsonp請求。
```
getJSONP() {
/* jsonp請求,需要服務器支持才行 */
const url = `http://localhost:8080/interface/testJSONP`;
this.httpClient.jsonp(url, "CallBack").subscribe((response: any) => {
console.log(response);
console.log(response.master);
});
}
```
服務器端接口對應的實現方式,這裏使用的JSONObject是阿里的fastjson
```
@RequestMapping("/testJSONP")
@ResponseBody
public String testJSONP(@RequestParam("CallBack") String callback) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("result","200");
jsonObject.put("master","charberming");
jsonObject.put("appname","app1");
return callback+"("+jsonObject+")";
}
```
這裏返回的數據必須是由前端請求url上帶着的callback參數拼接的json字符串格式,否則前端會報錯。在這裏由於我們在前端請求參數中設置的回調參數爲CallBack,所以在服務端程序中,我們要拿到的參數也是CallBack,保持一致。
注:主要是在學習angular過程中,出現跨域問題,網上找的辦法主要是在前端設置,比如說angular設置代理文件啓動,還有說使用nginx反向代理,前者說是能解決跨域問題,但是後者就太扯淡了,但還是都去驗證了下,沒成功。因此在這裏做一些總結,以節省道友時間。