上文 讲到浏览器因为安全的思考,设置了同源策略。这就是跨域的形成原因,也谈到了跨域的一些解决方案。
本文就是上篇文章的实操,好在项目开发时作为参考资料。
[TOC]
在开始之前,先说说实验环境:

前端采用 Vue3 搭建,端口为 8080

后端采用 Spring Boot 搭建,端口为 8888

后端测试接口:

  1. @RestController
  2. public class HelloController {
  3. @GetMapping("/hello")
  4. public Map hello(){
  5. HashMap result = new HashMap();
  6. result.put("message", "Hello World!");
  7. return result;
  8. }
  9. }

启动项目,测试一下:

  1. $ curl localhost:8888/hello
  2. Hello world

解决前后端跨域可以说就是解决 AJAX 跨域。上文说到四种跨域方案,其中 CORS代理服务器 为常见解决方案。

CORS

使用 CrossOrigin 注解

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

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

写在类上,表示该类的所有方法对应的接口浏览器都不会拦截。

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

写在方法上,表示该方法对应的接口浏览器不会拦截。

实现 WebMvcConfigurer

创建一个类 CorsConfig,使用 @Configuration 标识它为配置类;实现 WebMvcConfigurer,重写 addCorsMappings 方法:

  1. @Configuration
  2. public class CorsConfig implements WebMvcConfigurer {
  3. @Override
  4. public void addCorsMappings(CorsRegistry registry) {
  5. registry.addMapping("/**")
  6. .allowedOrigins("http://localhost:8080");
  7. }
  8. }

需要注意的是,如果设置 Access-Control-Allow-Origin*Access-Control-Allow-Credentials 就不能设置为 true。

Filter

创建一个类 CorsFilter,使用 @Configuration 标识它为配置类;实现 Filter,实现 doFilter 方法:

  1. @Configuration
  2. public class CorsFilter implements Filter {
  3. @Override
  4. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
  5. HttpServletResponse httpResponse = (HttpServletResponse) response;
  6. httpResponse.setHeader("Access-Control-Allow-Origin", "*");
  7. filterChain.doFilter(request, response);
  8. }
  9. }

更多 CORS 头部信息,可以 参考文档

测试

在前端代码中或者用浏览器打开前端监听的端口,输入以下 JavaScript 代码:

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

返回:

  1. {
  2. "message": "Hello World"
  3. }

代理服务器

nginx

在开发过程中,前端会自己起一个服务来调试代码。于是 nginx 可以监听 80 端口,分别反向代理前端服务和后台服务。

  1. server {
  2. listen 80;
  3. server_name 127.0.0.1;
  4. location / {
  5. proxy_pass http://127.0.0.1:8080;
  6. }
  7. location /api/ {
  8. proxy_pass http://127.0.0.1:8888/;
  9. }
  10. }

需要注意的是,后端反向代理端口后要加上符号 /。否则访问 127.0.0.1/api/hello 就会反向代理到 http://127.0.0.1:8888/api/hello 而不是 http://127.0.0.1:8888/hello

前端写完代码之后,可以将代码打包成静态文件,使用 nginx 来解析。

  1. server {
  2. listen 80;
  3. server_name 127.0.0.1;
  4. index index.html;
  5. root /home/kang/vue-demo/dist;
  6. location / {
  7. try_files $uri $uri/ /index.html;
  8. }
  9. location /api/ {
  10. proxy_pass http://127.0.0.1:8888/;
  11. }
  12. }

需要注意的是,如果前端使用 history 来模拟 url,那在代理的过程中需要重写跳转规则:try_files $uri $uri/ /index.html。该语句表示 URL 的跳转由 index.html 管理。删除导致前端项目路由失效且 nginx 响应 404。

测试

在前端项目中,或者用浏览器打开,输入下列代码:

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

返回:

  1. {
  2. "message": "Hello World"
  3. }

node

在根目录中打开 vue.config.js(如果没有就新建)。写下如下语句:

  1. module.exports = {
  2. devServer: {
  3. proxy: {
  4. "/api": {
  5. target: "http://localhost:8888",
  6. changeOrigin: true,
  7. pathRewrite: {
  8. '^/api': ''
  9. }
  10. }
  11. }
  12. }
  13. }

这里需要注意的是,这里的 pathRewrite,也就是路径重写。将 /api 前缀会去掉了。这样访问 /api/hello 才是 http://127.0.0.1:8888/hello,而不是 http://127.0.0.1:8888/hello

测试

在前端项目中,或者用浏览器打开,输入下列代码:

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

返回:

  1. {
  2. "message": "Hello World"
  3. }