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=%e8%b7%a8%e5%9f%9f%e4%b8%ad%e9%97%b4%e4%bb%b6
<?phpdeclare(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']);
虽然看起来有点不舒服,但是也是一个很好方案
个人建议是第一种,简单方便