跨域-實踐篇

飛機飛過天空發表於2021-01-11

上文 講到瀏覽器因為安全的思考,設定了同源策略。這就是跨域的形成原因,也談到了跨域的一些解決方案。

本文就上篇文章的實踐,在專案開發時好作為參考資料。

[TOC]

準備環境

在開始之前,先說說實驗環境:

前端採用 Vue3 搭建,埠為 8080

後端採用 Spring Boot 搭建,埠為 8888

後端測試介面:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public Map hello(){
        HashMap result = new HashMap();
        result.put("message", "Hello World!");
        return result;
    }
}

啟動專案,測試一下:

$ curl localhost:8888/hello
{"message":"Hello World"}

解決前後端跨域可以說就是解決 AJAX 跨域。上文說到四種跨域方案,其中 CORS代理伺服器 為常見解決方案。

CORS

使用 @CrossOrigin 註解

@CrossOrigin 可以用在類或者方法上:

@CrossOrigin(origins = "http://localhost:8080")
@RestController
public class HelloController

寫在類上,表示該類的所有方法對應的介面瀏覽器都不會攔截。

@GetMapping("/hello")
@CrossOrigin(origins = "http://localhost:8080")
public String hello()

寫在方法上,表示該方法對應的介面瀏覽器不會攔截。

實現 WebMvcConfigurer

建立一個類 CorsConfig,使用 @Configuration 標識它為配置類;實現 WebMvcConfigurer,重寫 addCorsMappings 方法:

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:8080");
    }
}

需要注意的是,如果設定 Access-Control-Allow-Origin*Access-Control-Allow-Credentials 就不能設定為 true。

Filter

建立一個類 CorsFilter,使用 @Configuration 標識它為配置類;實現 Filter,實現 doFilter 方法:

@Configuration
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        filterChain.doFilter(request, response);
    }
}

更多 CORS 頭部資訊,可以 參考文件

測試

在前端程式碼中或者用瀏覽器開啟前端監聽的埠,輸入以下 JavaScript 程式碼:

fetch('http://localhost:8888/hello')
.then(response => response.json())
.then(json => console.log(json))

返回:

{
    "message": "Hello World"
}

代理伺服器

nginx

在開發過程中,前端會自己起一個服務來除錯程式碼。於是 nginx 可以監聽 80 埠,分別反向代理前端服務和後臺服務。

server {
    listen 80;
    server_name 127.0.0.1;

    location / {
        proxy_pass http://127.0.0.1:8080;
    }

    location /api/ {
        proxy_pass http://127.0.0.1:8888/;
    }
}

需要注意的是,後端反向代理埠後要加上符號 /。否則訪問 127.0.0.1/api/hello 就會反向代理到 http://127.0.0.1:8888/api/hello 而不是 http://127.0.0.1:8888/hello

前端寫完程式碼之後,可以將程式碼打包成靜態檔案,使用 nginx 來解析。

server {
    listen 80;
    server_name 127.0.0.1;
    index index.html;
    root /home/kang/vue-demo/dist;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://127.0.0.1:8888/;
    }
}

需要注意的是,如果前端使用 history 來模擬 url,那在代理的過程中需要重寫跳轉規則:try_files $uri $uri/ /index.html。該語句表示 URL 的跳轉由 index.html 管理。刪除導致前端專案路由失效且 nginx 響應 404。

測試

在前端專案中,或者用瀏覽器開啟,輸入下列程式碼:

fetch('/api/hello')
.then(response => response.json())
.then(json => console.log(json))

返回:

{
    "message": "Hello World"
}

node

在根目錄中開啟 vue.config.js(如果沒有就新建)。寫下如下語句:

module.exports = {
    devServer: {
        proxy: {
            "/api": {
                target: "http://localhost:8888",
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
}

這裡需要注意的是,這裡的 pathRewrite,也就是路徑重寫。將 /api 字首會去掉了。這樣訪問 /api/hello 才是 http://127.0.0.1:8888/hello,而不是 http://127.0.0.1:8888/hello

測試

在前端專案中,或者用瀏覽器開啟,輸入下列程式碼:

fetch('/api/hello')
.then(response => response.json())
.then(json => console.log(json))

返回:

{
    "message": "Hello World"
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章