hyperf 学习 三、中间件

这篇具有很好参考价值的文章主要介绍了hyperf 学习 三、中间件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文档地址:Hyperf

中间件的原理就是请求时拦截并进行处理,再传到对应的回调,处理返回数据。

流程:request->middleware->response。

中间件在路由定义时配置。所以根据上一篇文章hyperf 学习 二、路由_lsswear的博客-CSDN博客

中注释路由Controller、AutoController可以配置options。

后来查了下代码,配置路由文件时,hyperf重写的addRoute中配置options也可以配置中间件。

public function addRoute($httpMethod, string $route, $handler, array $options = [])
    {
        $route = $this->currentGroupPrefix . $route;
        $routeDatas = $this->routeParser->parse($route);
        $options = $this->mergeOptions($this->currentGroupOptions, $options);
        foreach ((array) $httpMethod as $method) {
            $method = strtoupper($method);
            foreach ($routeDatas as $routeData) {
                $this->dataGenerator->addRoute($method, $routeData, new Handler($handler, $route, $options));
            }

            MiddlewareManager::addMiddlewares($this->server, $route, $method, $options['middleware'] ?? []);
        }
    }

一、定义全局中间件

        配置文件\config\autoload\middlewares.php

return [
    'http' => [
    ],
];

 http是server名字,server之后再研究。

这表明不管是路由还是中间件都是根据server分组的。

 'servers' => [
        [
            'name' => 'http',
            'type' => Server::SERVER_HTTP,
            'host' => '0.0.0.0',
            'port' => 9501,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
            ],
        ],
    ],

 server中可以配置类型比如http、socket、redis等。

二、定义局部中间件

       2.1  通过配置文件定义

use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Router\Router;

// 每个路由定义方法都可接收一个 $options 参数
Router::get('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [FooMiddleware::class]]);
Router::post('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [FooMiddleware::class]]);
Router::put('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [FooMiddleware::class]]);
Router::patch('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [FooMiddleware::class]]);
Router::delete('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [FooMiddleware::class]]);
Router::head('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [FooMiddleware::class]]);
Router::addRoute(['GET', 'POST', 'HEAD'], '/index', [\App\Controller\IndexController::class, 'index'], ['middleware' => [FooMiddleware::class]]);

// 该 Group 下的所有路由都将应用配置的中间件
Router::addGroup(
    '/v2', function () {
        Router::get('/index', [\App\Controller\IndexController::class, 'index']);
    },
    ['middleware' => [FooMiddleware::class]]
);

        2.2 通过注释定义

注释 说明 对应类
@Middleware 定义单个中间件时使用,在一个地方仅可定义一个该注解,不可重复定义 use Hyperf\HttpServer\Annotation\Middleware;
@Middlewares 定义多个中间件时使用,在一个地方仅可定义一个该注解,然后通过在该注解内定义多个 @Middleware 注解实现多个中间件的定义 use Hyperf\HttpServer\Annotation\Middlewares;
namespace App\Controller;

use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;

/**
 * @AutoController()
 * @Middleware(FooMiddleware::class)
 */
class IndexController
{
    public function index()
    {
        return 'Hello Hyperf.';
    }
}
<?php
namespace App\Controller;

use App\Middleware\BarMiddleware;
use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\Middlewares;

/**
 * @AutoController()
 * @Middlewares({
 *     @Middleware(FooMiddleware::class),
 *     @Middleware(BarMiddleware::class)
 * })
 */
class IndexController
{
    public function index()
    {
        return 'Hello Hyperf.';
    }
}

        2.3 定义方法级别中间件

namespace App\Controller;

use App\Middleware\BarMiddleware;
use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\Middlewares;

/**
 * @AutoController()
 * @Middlewares({
 *     @Middleware(FooMiddleware::class)
 * })
 */
class IndexController
{
    
    /**
     * @Middlewares({
     *     @Middleware(BarMiddleware::class)
     * })
     */
    public function index()
    {
        return 'Hello Hyperf.';
    }
}

三、中间件定义

先说明下hyperf是怎么调用自定义中间件。首先中间件调用是框架实现,实现可以是自定义,那么调用的入口肯定都是固定的,经过查询代码也确实如此。

自定义中间件入口函数process。

调用位置:Hyperf\Dispatcher\AbstractRequestHandler::handleRequest(),

调用顺序:

        Hyperf\HttpServer\Server::__construct()注入Hyperf\Dispatcher\HttpDispatcher\HttpDispatcher;

        HttpDispatcher::dispatch()调用Hyperf\Dispatcher\HttpRequestHandler::handle();

        HttpRequestHandler继承Hyperf\Dispatcher\AbstractRequestHandler并使用handle()调用handleRequest()。

Hyperf\HttpServer\Server调用是通过server.php设置,配置文件通过ConfigProvider:: __invoke()加载,hyperf每个模块都有ConfigProvider。__invoke()作用使类当作方法使用。根据ConfigProvider文件内容应该是和命令行有关。

查了会代码,没查明白/(ㄒoㄒ)/~~ 就看出模块里是config容器获取再获取对应的config,没看懂容器怎么加载和定义的。

#命令行
php ./bin/hyperf.php gen:middleware Auth/FooMiddleware


#对应类代码
declare(strict_types=1);

namespace App\Middleware\Auth;

use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface as HttpResponse;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class FooMiddleware implements MiddlewareInterface
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * @var HttpResponse
     */
    protected $response;

    public function __construct(ContainerInterface $container, HttpResponse $response, RequestInterface $request)
    {
        $this->container = $container;
        $this->response = $response;
        $this->request = $request;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 根据具体业务判断逻辑走向,这里假设用户携带的token有效
        $isValidToken = true;
        if ($isValidToken) {
            return $handler->handle($request);
        }

        return $this->response->json(
            [
                'code' => -1,
                'data' => [
                    'error' => '中间件验证token无效,阻止继续向下执行',
                ],
            ]
        );
    }
}

四、中间件执行顺序

执行顺序为:全局中间件 -> 类级别中间件 -> 方法级别中间件

五、全局更改请求和响应对象

这块官网文档提到PSR-7,PSR-7为用php实现的http消息接口协议。

hyperf/http-server用的psr/http-message,composer地址:psr/http-message - Packagist。

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

// $request 和 $response 为修改后的对象
$request = \Hyperf\Utils\Context::set(ServerRequestInterface::class, $request);
$response = \Hyperf\Utils\Context::set(ResponseInterface::class, $response);

psr-7请求内容对象不可变,hyperf实现后设置为可变。

六、自定义CoreMiddelWare的行为

根据代码从server开始创建CoreMiddleware,到AbstractRequestHandler在中间件找不到时调用CoreMiddleware::process(),CoreMiddleware::process根据$dispatched状态处理。

CoreMiddleware::dispatch()由server直接调用。

所以CoreMiddleware调用顺序为先是路径找不到或请求方法不允许再是中间件不存在。

#Hyperf\HttpServer\Server

public function initCoreMiddleware(string $serverName): void
    {
        $this->serverName = $serverName;
        $this->coreMiddleware = $this->createCoreMiddleware();
        $this->routerDispatcher = $this->createDispatcher($serverName);

        $config = $this->container->get(ConfigInterface::class);
        $this->middlewares = $config->get('middlewares.' . $serverName, []);
        $this->exceptionHandlers = $config->get('exceptions.handler.' . $serverName, $this->getDefaultExceptionHandler());
    }
 public function onRequest($request, $response): void
    {
        try {
            CoordinatorManager::until(Constants::WORKER_START)->yield();

            [$psr7Request, $psr7Response] = $this->initRequestAndResponse($request, $response);

            $psr7Request = $this->coreMiddleware->dispatch($psr7Request);
            /** @var Dispatched $dispatched */
            $dispatched = $psr7Request->getAttribute(Dispatched::class);
            $middlewares = $this->middlewares;
            if ($dispatched->isFound()) {
                $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod());
                $middlewares = array_merge($middlewares, $registeredMiddlewares);
            }

            $psr7Response = $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware);
        } catch (Throwable $throwable) {
            // Delegate the exception to exception handler.
            $psr7Response = $this->exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers);
        } finally {
            // Send the Response to client.
            if (! isset($psr7Response)) {
                return;
            }
            if (isset($psr7Request) && $psr7Request->getMethod() === 'HEAD') {
                $this->responseEmitter->emit($psr7Response, $response, false);
            } else {
                $this->responseEmitter->emit($psr7Response, $response, true);
            }
        }
    }

#Hyperf\Dispatcher\HttpDispatcher

public function dispatch(...$params): ResponseInterface
    {
        /**
         * @param RequestInterface $request
         * @param array $middlewares
         * @param MiddlewareInterface $coreHandler
         */
        [$request, $middlewares, $coreHandler] = $params;
        $requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container);
        return $requestHandler->handle($request);
    }

#Hyperf\Dispatcher\HttpRequestHandler

class HttpRequestHandler extends AbstractRequestHandler implements RequestHandlerInterface
{
    /**
     * Handles a request and produces a response.
     * May call other collaborating code to generate the response.
     */
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        return $this->handleRequest($request);
    }
}

#Hyperf\Dispatcher\AbstractRequestHandler 

 protected function handleRequest($request)
    {
        if (! isset($this->middlewares[$this->offset]) && ! empty($this->coreHandler)) {
            $handler = $this->coreHandler;
        } else {
            $handler = $this->middlewares[$this->offset];
            is_string($handler) && $handler = $this->container->get($handler);
        }
        if (! method_exists($handler, 'process')) {
            throw new InvalidArgumentException(sprintf('Invalid middleware, it has to provide a process() method.'));
        }
        return $handler->process($request, $this->next());
    }

#Hyperf\HttpServer\CoreMiddleware

public function dispatch(ServerRequestInterface $request): ServerRequestInterface
    {
        $routes = $this->dispatcher->dispatch($request->getMethod(), $request->getUri()->getPath());

        $dispatched = new Dispatched($routes);

        return Context::set(ServerRequestInterface::class, $request->withAttribute(Dispatched::class, $dispatched));
    }
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $request = Context::set(ServerRequestInterface::class, $request);

        /** @var Dispatched $dispatched */
        $dispatched = $request->getAttribute(Dispatched::class);

        if (! $dispatched instanceof Dispatched) {
            throw new ServerException(sprintf('The dispatched object is not a %s object.', Dispatched::class));
        }

        $response = null;
        switch ($dispatched->status) {
            case Dispatcher::NOT_FOUND:
                $response = $this->handleNotFound($request);
                break;
            case Dispatcher::METHOD_NOT_ALLOWED:
                $response = $this->handleMethodNotAllowed($dispatched->params, $request);
                break;
            case Dispatcher::FOUND:
                $response = $this->handleFound($dispatched, $request);
                break;
        }
        if (! $response instanceof ResponseInterface) {
            $response = $this->transferToResponse($response, $request);
        }
        return $response->withAddedHeader('Server', 'Hyperf');
    }

#FastRoute\Dispatcher\RegexBasedAbstract

abstract class RegexBasedAbstract implements Dispatcher
{
    /** @var mixed[][] */
    protected $staticRouteMap = [];

    /** @var mixed[] */
    protected $variableRouteData = [];

    /**
     * @return mixed[]
     */
    abstract protected function dispatchVariableRoute($routeData, $uri);

    public function dispatch($httpMethod, $uri)
    {
        if (isset($this->staticRouteMap[$httpMethod][$uri])) {
            $handler = $this->staticRouteMap[$httpMethod][$uri];
            return [self::FOUND, $handler, []];
        }

        $varRouteData = $this->variableRouteData;
        if (isset($varRouteData[$httpMethod])) {
            $result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri);
            if ($result[0] === self::FOUND) {
                return $result;
            }
        }

        // For HEAD requests, attempt fallback to GET
        if ($httpMethod === 'HEAD') {
            if (isset($this->staticRouteMap['GET'][$uri])) {
                $handler = $this->staticRouteMap['GET'][$uri];
                return [self::FOUND, $handler, []];
            }
            if (isset($varRouteData['GET'])) {
                $result = $this->dispatchVariableRoute($varRouteData['GET'], $uri);
                if ($result[0] === self::FOUND) {
                    return $result;
                }
            }
        }

        // If nothing else matches, try fallback routes
        if (isset($this->staticRouteMap['*'][$uri])) {
            $handler = $this->staticRouteMap['*'][$uri];
            return [self::FOUND, $handler, []];
        }
        if (isset($varRouteData['*'])) {
            $result = $this->dispatchVariableRoute($varRouteData['*'], $uri);
            if ($result[0] === self::FOUND) {
                return $result;
            }
        }

        // Find allowed methods for this URI by matching against all other HTTP methods as well
        $allowedMethods = [];

        foreach ($this->staticRouteMap as $method => $uriMap) {
            if ($method !== $httpMethod && isset($uriMap[$uri])) {
                $allowedMethods[] = $method;
            }
        }

        foreach ($varRouteData as $method => $routeData) {
            if ($method === $httpMethod) {
                continue;
            }

            $result = $this->dispatchVariableRoute($routeData, $uri);
            if ($result[0] === self::FOUND) {
                $allowedMethods[] = $method;
            }
        }

        // If there are no allowed methods the route simply does not exist
        if ($allowedMethods) {
            return [self::METHOD_NOT_ALLOWED, $allowedMethods];
        }

        return [self::NOT_FOUND];
    }
}

#Hyperf\HttpServer\Router\Dispatched

public function __construct(array $array)
    {
        $this->status = $array[0];
        switch ($this->status) {
            case Dispatcher::METHOD_NOT_ALLOWED:
                $this->params = $array[1];
                break;
            case Dispatcher::FOUND:
                $this->handler = $array[1];
                $this->params = $array[2];
                break;
        }
    }

    public function isFound(): bool
    {
        return $this->status === Dispatcher::FOUND;
    }

自定义

declare(strict_types=1);

namespace App\Middleware;

use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Utils\Contracts\Arrayable;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class CoreMiddleware extends \Hyperf\HttpServer\CoreMiddleware
{
    /**
     * Handle the response when cannot found any routes.
     *
     * @return array|Arrayable|mixed|ResponseInterface|string
     */
    protected function handleNotFound(ServerRequestInterface $request)
    {
        // 重写路由找不到的处理逻辑
        return $this->response()->withStatus(404);
    }

    /**
     * Handle the response when the routes found but doesn't match any available methods.
     *
     * @return array|Arrayable|mixed|ResponseInterface|string
     */
    protected function handleMethodNotAllowed(array $methods, ServerRequestInterface $request)
    {
        // 重写 HTTP 方法不允许的处理逻辑
        return $this->response()->withStatus(405);
    }
}


#定义重写
#config/autoload/dependencies.php
return [
    Hyperf\HttpServer\CoreMiddleware::class => App\Middleware\CoreMiddleware::class,
];

七、常用中间件

        7.1 跨域中间件

declare(strict_types=1);

namespace App\Middleware;

use Hyperf\Utils\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')
            // Headers 可以根据实际情况进行改写。
            ->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization');

        Context::set(ResponseInterface::class, $response);

        if ($request->getMethod() == 'OPTIONS') {
            return $response;
        }

        return $handler->handle($request);
    }
}

        nginx跨域

location / {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
}

        apache跨域

    Header always set Access-Control-Allow-Origin "*"
    Header always set Access-Control-Allow-Methods "POST,GET,OPTIONS"
    Header always set Access-Control-Allow-Headers "Content-Type,token"
    Header always set Access-Control-Max-Age 1728000
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]

跨域的话用反代理应该也能解决问题。

比如前后端分离,做前端的配置,可以反代理后端的域名。文章来源地址https://www.toymoban.com/news/detail-523544.html

到了这里,关于hyperf 学习 三、中间件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 消息中间件Kafuka学习——初次配置使用

    MQ本质 : 例如 ActiveMQ、RabbitMQ、RocketMQ 等中间件。采用这种模型,本质就是将要推送的数据,不在存放在当前应用程序的内存中,而是将数据存放到另一个专门负责数据处理的应用程序中,从而实现服务解耦。 kafuka模型 : 如果你看不懂这些概念没关系,我会带着大家一起梳理

    2023年04月10日
    浏览(36)
  • Python学习(3)- 消息中间件rabbitmq

    消息队列中间件是分布式系统中重要的组件,主要解决 应用解耦 , 异步消息 , 流量削锋 等问题,实现高性能,高可用,可伸缩和最终一致性架构 rabbitmq 简单模式 生产者: 链接rabbitmq 创建队列 向指定的队列中插入数据 交换机模式 发布订阅模式 模式 模糊匹配模式

    2024年02月22日
    浏览(51)
  • 六、用 ChatGPT 学习 Eureka 中间件源码

    目录 一、实验介绍 二、背景 三、ChatGPT 帮助分析 Eureka 源码 3.1 Eureka 源码在哪

    2024年02月16日
    浏览(46)
  • express学习笔记5 - 自定义路由异常处理中间件

    修改router/index.js,添加异常处理中间件 完整代码 创建 utils/constant:(为了方便后期统一维护,单独拉出来定义) 然后刷新http://localhost:8000/user  这就完成了

    2024年02月14日
    浏览(48)
  • Nginx 学习(十)高可用中间件的配置与实现

    调度器出现单点故障,如何解决? Keepalived实现了高可用集群 Keepalived最初是为LVS设计的,专门监控各服务器节点的状态 Keepalived后来加入了VRRP功能,防止单点故障 Keepalived检测每个服务器节点状态,当服务器节点异常或工作出现故障, Keepalived将故障节点从集群系统中剔除,故

    2024年02月09日
    浏览(110)
  • Go学习第十七章——Gin中间件与路由

    Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等 即比如,如果访问一个网页的话,不管访问什么路径都需要进行登录,

    2024年02月07日
    浏览(47)
  • 爬虫学习 Scrapy中间件&代理&UA随机&selenium使用

    控制台操作 (百度只起个名 scrapy startproject mid scrapy genspider baidu baidu.com setting.py内 运行 scrapy crawl baidu middlewares.py 中间件 先看下载器中间件 重点在 process_request 在引擎将请求的信息交给下载器之前,自动的调用该方法 process_response… process_exception 异常 (看名就知道了…) spider

    2024年03月23日
    浏览(62)
  • RabbitMQ-消息中间件学习记录(what-how-why)

    什么是消息中间件 简单的来说就是消息队列中间件,生产者发送消息到中间件,消息中间件用于 保存消息并发送消息到消费者。 消息中间件RabbitMQ的基本组件 1)producer -生产者 2)customer -消费者 3)broker (经纪人)- MQ服务器,管理消息对列、消息及相关消息。(接收并存储生

    2024年02月13日
    浏览(47)
  • 《分布式中间件技术实战:Java版》学习笔记(一):抢红包

    数据库建表 (1)red_send_record 记录用户发送了若干总金额的若干个红包。 (2)red_detail 记录用户发送的红包被分成的小红包金额。 (3)red_rob_record 记录用户抢到的红包金额。 随机生成红包金额 红包金额的最小单位是分,将红包金额放大100倍到int类型(为了方便生成随机数),保证

    2024年02月10日
    浏览(54)
  • 消息中间件学习笔记--RabbitMQ(二、模式,一次违反常规的Java大厂面试经历

    .Fanout:转发消息到所有绑定队列 比较常用的是Direct、Topic、Fanout. Fanout 这种Fanout模式不处理路由键,只·需要简单的将队列绑定到exchange上,一个发送到exchange的消息都会被转发到与该exchange绑定的所有队列上。很像广播子网,每台子网内的主机都获得了一份复制的消息。Fan

    2024年04月09日
    浏览(99)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包