跨域-实践篇

上文 讲到浏览器因为安全的思考,设置了同源策略。这就是跨域的形成原因,也谈到了跨域的一些解决方案。

本文就上篇文章的实践,在项目开发时好作为参考资料。

[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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!