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=%e8%b7%a8%e5%9f%9f%e4%b8%ad%e9%97%b4%e4%bb%b6

<?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']);
虽然看起来有点不舒服,但是也是一个很好方案

个人建议是第一种,简单方便

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值