hyperf跨域问题
2024年4月25日10:11:30
前段时间写完了hyperf的cms之后,回头写hyperf的一些文章或者笔记,发现hyperf和laravel真的很像,又有swoole的协程使用,真的很舒服,还有微服务。
官方推荐的是方式就是使用中间件,但是注意,你在路由上使用的时候,中间件是无法实现跨域的,因为在路由组件会抛出一个HttpException
,请求OPTIONS
返回405
状态码。
方案一:
https://hyperf.wiki/3.1/#/zh-cn/middleware/middleware?id=跨域中间件
<?php
declare(strict_types=1);
namespace App\Middleware;
use Hyperf\Context\Context;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class CorsMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = Context::get(ResponseInterface::class);
$response = $response->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Credentials', 'true')
->withHeader('Access-Control-Allow-Methods', '*')
->withHeader('Access-Control-Allow-Headers', '*');
Context::set(ResponseInterface::class, $response);
if ($request->getMethod() == 'OPTIONS') {
return $response->withStatus(204)->withBody(new SwooleStream(''));
}
return $handler->handle($request);
}
}
新建一个CorsMiddleware 放在hyperf/config/autoload/middlewares.php
的里面加入中间件
方案二:在nginx的反向代理利加入跨域配置
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Allow-Headers *;
if ($request_method = 'OPTIONS') {
return 204;
}
}
方案三:如果你现在路由上使用跨域中间件
新建一个hyperf/app/Exception/Handler/HttpExceptionHandler.php
把文件加入到 hyperf/config/autoload/exceptions.php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'handler' => [
'http' => [
Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class,
// App\Exception\Handler\HttpExceptionHandler::class,
App\Exception\Handler\AppExceptionHandler::class,
],
],
];
替换Hyperf的Handler
declare(strict_types=1);
namespace App\Exception\Handler;
use Hyperf\Context\Context;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Swow\Psr7\Message\ResponsePlusInterface;
use Throwable;
class HttpExceptionHandler extends ExceptionHandler
{
public function __construct(protected StdoutLoggerInterface $logger, protected FormatterInterface $formatter)
{
}
/**
* Handle the exception, and return the specified result.
* @param HttpException $throwable
*/
public function handle(Throwable $throwable, ResponsePlusInterface $response)
{
$this->logger->debug($this->formatter->format($throwable));
$this->stopPropagation();
$request = Context::get(ServerRequestInterface::class);
if ($request->getMethod() == 'OPTIONS') {
return $response->setStatus(204)
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Credentials', 'true')
->withHeader('Access-Control-Allow-Headers', '*')
->withHeader('Access-Control-Allow-Methods', '*')
->setBody(new SwooleStream(''));
}
return $response->setStatus($throwable->getStatusCode())->setBody(new SwooleStream($throwable->getMessage()));
}
/**
* Determine if the current exception handler should handle the exception.
*
* @return bool If return true, then this exception handler will handle the exception,
* If return false, then delegate to next handler
*/
public function isValid(Throwable $throwable): bool
{
return $throwable instanceof HttpException;
}
}
这里稍微改一下也可以实现跨域
方案四:在路由上加上OPTIONS,也是需要配合中间件跨域,才能使用的,这样就可以在路由上使用跨域中间件
Router::addGroup('/api/admin', function () {
Router::post('/login', [\App\Controller\Admin\IndexController::class, 'login']);
Router::post('/getCaptcha', [\App\Controller\Admin\IndexController::class, 'getCaptcha']);
Router::post('/uploadPic', [\App\Controller\Admin\IndexController::class, 'uploadPic']);
Router::post('/uploadFile', [\App\Controller\Admin\IndexController::class, 'uploadFile']);
});
改成:
Router::addRoute(['OPTIONS', 'POST'],'/login', [\App\Controller\Admin\IndexController::class, 'login']);
虽然看起来有点不舒服,但是也是一个很好方案
个人建议是第一种,简单方便
QQ一群 247823727
QQ二群 166427999
博客文件如果不能下载请进群下载
如果公司项目有技术瓶颈问题,请联系↓↓
如果需要定制系统开发服务,请联系↓↓
技术服务QQ: 903464207
QQ二群 166427999
博客文件如果不能下载请进群下载
如果公司项目有技术瓶颈问题,请联系↓↓
如果需要定制系统开发服务,请联系↓↓
技术服务QQ: 903464207