上文 讲到浏览器因为安全的思考,设置了同源策略。这就是跨域的形成原因,也谈到了跨域的一些解决方案。
本文就是上篇文章的实操,好在项目开发时作为参考资料。
[TOC]
在开始之前,先说说实验环境:
前端采用 Vue3 搭建,端口为
8080。后端采用 Spring Boot 搭建,端口为
8888。
后端测试接口:
@RestControllerpublic class HelloController {@GetMapping("/hello")public Map hello(){HashMap result = new HashMap();result.put("message", "Hello World!");return result;}}
启动项目,测试一下:
$ curl localhost:8888/helloHello world
解决前后端跨域可以说就是解决 AJAX 跨域。上文说到四种跨域方案,其中 CORS 和 代理服务器 为常见解决方案。
CORS
使用 CrossOrigin 注解
@CrossOrigin 可以用在类或者方法上:
@CrossOrigin(origins = "http://localhost:8080")@RestControllerpublic class HelloController
写在类上,表示该类的所有方法对应的接口浏览器都不会拦截。
@GetMapping("/hello")@CrossOrigin(origins = "http://localhost:8080")public String hello()
写在方法上,表示该方法对应的接口浏览器不会拦截。
实现 WebMvcConfigurer
创建一个类 CorsConfig,使用 @Configuration 标识它为配置类;实现 WebMvcConfigurer,重写 addCorsMappings 方法:
@Configurationpublic class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://localhost:8080");}}
需要注意的是,如果设置
Access-Control-Allow-Origin为*,Access-Control-Allow-Credentials就不能设置为 true。
Filter
创建一个类 CorsFilter,使用 @Configuration 标识它为配置类;实现 Filter,实现 doFilter 方法:
@Configurationpublic class CorsFilter implements Filter {@Overridepublic 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"}
