前言
本文将讲解前后端项目中跨域问题的常见解决方案,其中后端基于SpringBoot
,前端使用了jQuery
、axios
等框架用于实战代码的讲解。本文将不涉及跨域
的解释和SpringBoot
等框架,或者是Nginx
的使用,将主要讲解前后端分离项目中跨域问题的解决,不过如果你遇到了问题,也欢迎一起交流学习。
跨域解决
JSONP
方式
这种方式只能用于Get
请求,因此如果需要以Post
请求方式获取数据,则可以先看后面CORS
解决方式,以下就讲解两种基于JSONP
原理的解决方案,首先先看后端的接口:
@RestController
@RequestMapping("/api/customer")
public class HelloController {
@GetMapping("/getAllCustomerInfo")
public String getAllCustomerInfo() {
// 返回的数据必须包含在 getAllCustomerInfo() 字符串中
// 以便在返回到前端后,可以自动执行回调函数,获取数据
// getAllCustomerInfo() 与前端的回调函数名对应
return "getAllCustomerInfo(" + getCustomerList() + ")";
}
private String getCustomerList() {
List<Customer> list = new ArrayList<>(Arrays.asList(
new Customer(1, "张三", "123456"),
new Customer(2, "李四", "654321"),
new Customer(3, "王五", "123123")
));
return new Gson().toJson(list);
}
}
下面就来分别讲解以下两种方式的解决方案:
js
原生解决方案
// 首先设置 src 并将结点添加到 <head> 中
const script = document.createElement('script')
script.src = 'http://localhost:8080/api/customer/getAllCustomerInfo'
document.head.appendChild(script)
// 回调函数名与接口中返回的名字对应
const getAllCustomerInfo = res => {
console.table(res, ['id', 'username', 'password'])
}
通过以上设置就可以在Console
中看到以下结果:
jQuery
的ajax
解决方案
// 记得需要引入 jQuery
<script src="https://cdn.staticfile.org/jquery/1.10.0/jquery.js"></script>
$.ajax({
url: 'http://localhost:8080/api/customer/getAllCustomerInfo',
type: 'get',
dataType: 'jsonp',
jsonpCallback: "getAllCustomerInfo"
})
// 回调函数同上
const getAllCustomerInfo = res => {
console.table(res, ['id', 'username', 'password'])
}
CORS
解决方案
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求,详细解释请看链接。
- 后端接口中进行设置
通过利用CORS
,我们在前端只需要利用正常的ajax
请求方式即可,无需再设置回调函数,在这里我们使用了axios
,在展示后端代码之前,我们先看后端代码的改变,为了区别于JSONP
只能进行Get
请求,我们这里将接口改成了Post
:
@RestController
@RequestMapping("/api/customer")
public class HelloController {
@PostMapping("/getAllCustomerInfo")
public List<Customer> getAllCustomerInfo(HttpServletResponse response) {
// 设置响应头
response.setHeader("Access-Control-Allow-Origin", "*");
// 不再需要将结果放在回调函数中
return new ArrayList<>(Arrays.asList(
new Customer(1, "张三", "123456"),
new Customer(2, "李四", "654321"),
new Customer(3, "王五", "123123")
));
}
}
然后前端代码直接使用ajax
请求即可:
// 首先需要引入 axios
<script src="https://cdn.staticfile.org/axios/0.1.0/axios.js"></script>
axios.post('http://localhost:8080/api/customer/getAllCustomerInfo')
.then(res => {
console.table(res, ['id', 'username', 'password'])
})
Nginx
反向代理
关于反向代理的知识,这里不做详细解析。通过使用Nginx
的反向代理,我们在后端接口中就可以去掉响应头的代码设置:
@RestController
@RequestMapping("/api/customer")
public class HelloController {
@PostMapping("/getAllCustomerInfo")
public List<Customer> getAllCustomerInfo() {
return new ArrayList<>(Arrays.asList(
new Customer(1, "张三", "123456"),
new Customer(2, "李四", "654321"),
new Customer(3, "王五", "123123")
));
}
}
然后是nginx.conf
中的修改:
server {
# 监听80端口, 前端不再直接访问8080端口, 改为访问80端口即可
listen 80;
server_name localhost;
location / {
root html;
# 添加头
add_header Access-Control-Allow-Origin *;
# 代理转发到8080后端端口
proxy_pass http://localhost:8080;
index index.html index.htm;
}
}
然后再将前端中访问的接口修改为80:
// 首先需要引入 axios
<script src="https://cdn.staticfile.org/axios/0.1.0/axios.js"></script>
// 80为 http 默认端口,可省略
axios.post('http://localhost/api/customer/getAllCustomerInfo')
.then(res => {
console.table(res, ['id', 'username', 'password'])
})
然后开启Nginx
服务器,Console
中再次出现了我们想看到的结果:
总结
本文并没有包括跨域问题的所有解决方案,不过相信对于大部分的跨域问题,都已经可以解决了,本文在之后也可能会继续更新,讲解更多种解决跨域问题的方法