一、背景
最近在弄毕业设计啦,采用CodeIgniter4 Vue3来做的,前后端分离项目,首先便是跨域问题。一顿搜索无果后,自己折腾了一个解决方案,希望能帮助到看到这篇文章的你。
二、跨域问题
由于浏览器的 同源策略 限制,使用前后端分离的模式下,前端和后端的域名一般都不是一样的,在我的项目中,前端是使用二级域名,而后端是使用三级域名,此时前后端就不同源了,就产生了跨域问题。
同源即两个页面具有相同的协议(protocol),主机(host)和端口号(port)
下表即我目前遇到的情况
域名 | 域名级别 | 框架 | |
---|---|---|---|
前端 | example.com | 二级域名 | Vue3 |
后端 | api.example.com | 三级域名 | CodeIgniter4 |
三、解决方法
1.问题
在前端往后端发送请求时,控制台会输出跨域报错,无法拿到数据。此时控制台会显示Access-Control-Allow-Origin不包含当前发送请求页面的相关提示。
2.解决思路
最常见的方法便是把响应头设置为
Access-Control-Allow-Origin: *
但这样每个接口都要设置一遍,会比较麻烦。
此时我们需要用到CodeIgniter4中的控制器过滤器里面的 前置过滤器 。
前置过滤器的官方文档
然后在前置过滤器中完成响应头的设定即可。
3.实现
(1)在app下找到Filters文件夹,如果没有,请先创建;
(2)在Filters文件夹下创建CorsFilter.php
文件。
(3)写入以下代码
代码语言:php复制<?php
namespace AppFilters;
use CodeIgniterHTTPRequestInterface;
use CodeIgniterHTTPResponseInterface;
use CodeIgniterFiltersFilterInterface;
class CorsFilter implements FilterInterface
{
// 前置过滤器
public function before(RequestInterface $request, $arguments = null)
{
$response = service('response');
$response->setHeader('Access-Control-Allow-Origin', '*');
}
//--------------------------------------------------------------------
// 后置过滤器
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// Do something here
}
}
(4)在app/Config/Filters.php
中配置我们刚刚创建的过滤器。大功告成!
// ----上面代码省略,无需修改----
public $aliases = [
'csrf' => CSRF::class,
'toolbar' => DebugToolbar::class,
'honeypot' => Honeypot::class,
'cors' => CorsFilter::class
];
public $globals = [
'before' => [
// 配置生效页面,except里面配置的时不生效页面
'cors'=> ['except' => ['/yourPage1', '/yourPage2/detail']]
],
'after' => [
],
];
// ----下面代码省略,无需修改----
4.自定义请求头(无此需求可跳过)
由于我的项目需要自定义请求头,这时候又得另外处理一下。
使用 自定义请求头 时,前端(客户端浏览器)会先发出一个OPTIONS
请求,来判断是否可用,如果这时候没有进行设置的话,同样也是无法完成跨域的。
在上面的基础上加入以下代码
代码语言:php复制// 允许各种方法
$response->setHeader('Access-Control-Allow-Method', '*');
// 允许User-Token请求头(如果你是其他的自定义头,请更改)
$response->setHeader('Access-Control-Allow-Headers', 'User-Token');
// 判断请求的方法是否为OPTIONS,如果为OPTIONS,则返回200OK,表示服务器可以接受该方法
if($request->getMethod(FALSE)=='options'){
return $response->setStatusCode(200,'OK');
}
加入处理自定义请求头的完整控制器过滤器代码
代码语言:php复制<?php
namespace AppFilters;
use CodeIgniterHTTPRequestInterface;
use CodeIgniterHTTPResponseInterface;
use CodeIgniterFiltersFilterInterface;
class CorsFilter implements FilterInterface
{
// 前置过滤器
public function before(RequestInterface $request, $arguments = null)
{
$response = service('response');
$response->setHeader('Access-Control-Allow-Origin', '*');
$response->setHeader('Access-Control-Allow-Method', '*');
$response->setHeader('Access-Control-Allow-Headers', 'User-Token');
if($request->getMethod(FALSE)=='options'){
return $response->setStatusCode(200,'OK');
}
}
//--------------------------------------------------------------------
// 后置过滤器
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// Do something here
}
}
四、注意事项
在创建过滤器文件中,不能删除后置过滤器
错误代码
代码语言:php复制<?php
// 错误代码示例!!!!
namespace AppFilters;
use CodeIgniterHTTPRequestInterface;
use CodeIgniterHTTPResponseInterface;
use CodeIgniterFiltersFilterInterface;
class CorsFilter implements FilterInterface
{
// 前置过滤器
public function before(RequestInterface $request, $arguments = null)
{
$response = service('response');
$response->setHeader('Access-Control-Allow-Origin', '*');
}
}
// 错误代码示例!!!!
正确代码
代码语言:php复制<?php
namespace AppFilters;
use CodeIgniterHTTPRequestInterface;
use CodeIgniterHTTPResponseInterface;
use CodeIgniterFiltersFilterInterface;
class CorsFilter implements FilterInterface
{
// 前置过滤器
public function before(RequestInterface $request, $arguments = null)
{
$response = service('response');
$response->setHeader('Access-Control-Allow-Origin', '*');
}
//--------------------------------------------------------------------
// 后置过滤器
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// Do something here
}
}