1 问题背景
通常我们在开发web应用的时候,需要将前端项目和后端项目部署在不同的站点上,前端通过调用后端的api获取和操作数据。所谓前端和后端站点不同,是指站点的URL源不同。组成URL源的协议、域名、端口(即 协议://域名:端口组合)只要有一个不同,即被认为不同源。如https://abc.somewebsite.com:8080
和https://abc.somewebsite.com:8088
,虽然协议和域名是相同的,但由于端口不同, 所以不同源。
浏览器出于安全考虑,设计了同源策略(Same-Origin Policy),用于防止不同源之间的恶意行为,确保数据的安全。如果一个请求的目标地址的源与当前页面的源不同,则将会触发跨域请求,浏览器的同源策略会阻止这样的请求,出现404错误。通过直接在浏览器开发者工具可以看到,跨域请求被浏览器限制了。如下图:
2 解决方案
解决跨域请求的方法通常有两种,一是使用node服务转发,二是nginx代理,本质上都是建立一个代理服务器,然后将客户端的请求转发给api服务器。在开发阶段,一般使用node服务端转发方式(如过使用vue框架,vue内中了node进程);而在生产环境中,则需要设置nginx代理。下面就分别进行详尽说明操作步骤。
3 项目准备
本文章只是为了说明如何处理跨域请求404问题,所以IDE、运行环境的准备不在本文范围之内。为简化说明,我们的示例只提供简单的接口。
3.1 服务端准备
新建一个springboot项目demo-springboot,实现一个控制器TestController,只提供一个接口hello,如下:
package com.tarlong.demospringboot.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
public String hello() {
return "Hello world";
}
}
3.2 客户端准备
新建一个vue3项目,安装axios并实现一个网络请求模块request.js,如下:
import axios from "axios";
const service = axios.create({
baseURL: "/api",
timeout: 30000,
});
// request 拦截器
service.interceptors.request.use(
async (config) => {
return config;
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);
// response 拦截器
service.interceptors.response.use(
(response) => {
const { data } = response;
if (data.code !== 200) {
console.error("Error:", data.message);
return Promise.reject("Error");
} else {
return data;
}
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);
export const get = (url, params) => {
return service.get(url, { params });
};
export const post = (url, data) => {
return service.post(url, data);
};
export default service;
在App.vue主页上添加一个按钮,用于向服务器发起网络请求,其javascript代码如下:
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import { get } from "@/utils/request";
const onHello = () => {
get("/test/hello")
.then((response) => {
console.log("response", response);
})
.catch((error) => {
console.log("error", error);
});
};
</script>