Hyperf 里中间件使用方式和中间件的流程原理 包括通过 @Middlewares 和 @Middleware 注解来注册中间件

官网解释
这里的中间件指的是 “中间件模式”,该功能属于 hyperf/http-server 组件内的一项主要功能,主要用于编织从 请求(Request) 到 响应(Response) 的整个流程,该功能完全基于 PSR-15 实现。
原理
中间件主要用于编织从 请求(Request) 到 响应(Response) 的整个流程,通过对多个中间件的组织,使数据的流动按我们预定的方式进行,中间件的本质是一个 洋葱模型,我们通过一个图来解释它:

在这里插入图片描述
图中的顺序为按照 Middleware 1 -> Middleware 2 -> Middleware 3 的顺序组织着,我们可以注意到当中间的横线穿过 内核 即 Middleware 3 后,又回到了 Middleware 2,为一个嵌套模型,那么实际的顺序其实就是:
Request -> Middleware 1 -> Middleware 2 -> Middleware 3 -> Middleware 2 -> Middleware 1 -> Response
重点放在 核心 即 Middleware 3,它是洋葱的分界点,分界点前面的部分其实都是基于 请求(Request) 进行处理,而经过了分界点时,内核 就产出了 响应(Response) 对象,也是 内核 的主要代码目标,在之后便是对 响应(Response) 进行处理了,内核 通常是由框架负责实现的,而其它的就由您来编排了。

在这里插入图片描述

看到中间件的时候我有点迷糊,因为和前面的AOP有点类似,但是后台一想又不对, 有差距,aop是切面,是任意切到哪个面,但是中间件是洋葱模型,对于请求和返回属于层层嵌套的,不像AOP,还是有差距,我不知道我理解的对不对, 如果有更明白的可以私聊给我讲解一下

实例类中建立中间件

**先建立3个中间件 通过命令 **
php bin/hyperf.php gen:middleware 中间件名称
php bin/hyperf.php gen:middleware FooMiddleware
php bin/hyperf.php gen:middleware BarMiddleware
php bin/hyperf.php gen:middleware BarzMiddleware
1.

<?php

declare(strict_types=1);

namespace App\Middleware;

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

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

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        var_dump('1');
        $response =  $handler->handle($request);
        var_dump('4');
        return $response;
    }
}
<?php

declare(strict_types=1);

namespace App\Middleware;

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

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

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        var_dump('2');
        $response =  $handler->handle($request);
        var_dump('5');
        return $response;
    }
}
<?php

declare(strict_types=1);

namespace App\Middleware;

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

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

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        var_dump('3');
        $reponse = $handler->handle($request);
        var_dump('6');
        return $reponse;
    }
}

使用中间件的控制器代码

<?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
 */
namespace App\Controller\User;

use App\Controller\AbstractController;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\Middlewares;
use App\Middleware\BarMiddleware;
use App\Middleware\BarzMiddleware;
use App\Middleware\FooMiddleware;
/**
 * @AutoController()
 * @Middlewares(
 *  @MiddleWare(FooMiddleware::class),
 *  @MiddleWare(BarMiddleware::class),
 *  @MiddleWare(BarzMiddleware::class)
 * )
 */
class IndexController extends AbstractController
{

    public function index()
    {
       return 'index';
    }
}

运行结果

在这里插入图片描述

个人观点
由上面的例子可见,他的运行顺序是根据洋葱模型的形式
Foo -> Bar -> Barz -> 请求代码 -> 返回response-> Barz -> Bar -> Foo

中间遇到的问题

我想改变最后的返回值,那么先获取到返回的body。,当重写值得时候报这个错, 我查询后意思应该是这样不符合ps7的规范,我是这么理解的。那么教程中给出的结解决方法是在这里插入图片描述
解决方法
这个应该是swoole提供的-不怎么懂希望看到这个文章的人懂得话给我讲讲
在这里插入图片描述

改写返回信息

<?php

declare(strict_types=1);

namespace App\Middleware;

use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

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

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        var_dump('3');
        $reponse = $handler->handle($request);
        //获取返回的body
        var_dump('6');
        $body = $reponse->getBody()->getContents();
       
        return $reponse->withBody(new SwooleStream($body.'foo'));
    }
}

返回信息
在这里插入图片描述

方法中定义中间件

<?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
 */
namespace App\Controller\User;

use App\Controller\AbstractController;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\Middlewares;
use App\Middleware\BarMiddleware;
use App\Middleware\BarzMiddleware;
use App\Middleware\FooMiddleware;
/**
 * @AutoController()
 * @Middlewares(
 *  @MiddleWare(FooMiddleware::class),
 *  @MiddleWare(BarzMiddleware::class)
 * )
 */
class IndexController extends AbstractController
{
    /**
     * @MiddleWare(BarMiddleware::class)
     */
    public function index()
    {
       return 'index';
    }
}


执行结果
在这里插入图片描述

执行过程
Foo -> Barz->Bar->请求代码 -> 返回response->Bar->Barz->Foo

由此可见执行过程在类里面声明的中间件是要比方法中先执行的

定义全局中间件
遇到的问题 类中使用middlewars引入中间件发生错误
在这里插入图片描述
如果使用middlewars里面就有一个的情况下, 那么就会出现问题。我感觉因为是s里面不能是一个
必须使用一个以上

在这里插入图片描述

<?php

declare(strict_types=1);

namespace App\Middleware;

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

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

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        var_dump('7');
        $response =  $handler->handle($request);
        var_dump('8');
        return  $response;
    }
}

结果

在这里插入图片描述
结论是显示全局->类->方法

疑问
发现home全局的在最后又调了一次。没有找到问题。如果有人发现的话可以告诉我一下

通过配置文件定义或其他更多方法看文档

对于接收值在中间件中是互通的, 但是在代码中接收是接收不到的, 其实是因为最终接收的request和中间件的协程不是一个,应该把最终的协程覆盖

中间件代码

<?php

declare(strict_types=1);

namespace App\Middleware;

use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Utils\Context;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

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

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        var_dump('3');
        //覆盖原先的协程,并赋值
        $request = Context::override(ServerRequestInterface::class, function() use($request) {
            return $request->withAttribute('barz', 1);
        });
        $reponse = $handler->handle($request);
        //获取返回的body
        var_dump('6');
        $body = $reponse->getBody()->getContents();
       
        return $reponse->withBody(new SwooleStream($body.'barz'));
    }
}

控制器代码

<?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
 */
namespace App\Controller\User;

use App\Controller\AbstractController;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\Middlewares;
use App\Middleware\BarMiddleware;
use App\Middleware\BarzMiddleware;
use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Contract\RequestInterface;

/**
 * @AutoController()
 * @Middlewares(
 *  @MiddleWare(FooMiddleware::class),
 *  @MiddleWare(BarzMiddleware::class)
 * )
 */
class IndexController extends AbstractController
{
    /**
     * @MiddleWare(BarMiddleware::class)
     */
    public function index(RequestInterface $reques)
    {
        //获取中间件传过来的值
        $barz = $reques->getAttribute('barz');
        var_dump($barz);
        return 'index';
    }
}

返回结果
在这里插入图片描述

### 回答1: 你可以使用自定义的 Scrapy 中间件来处理 response.follow() 请求。 首先,在你的 Scrapy 项目中创建一个中间件文件,然后在这个文件中定义一个新的中间件类。在这个类中,你需要实现以下三个方法: ``` from scrapy import signals class MyMiddleware: def __init__(self): # 在这初始化中间件 pass @classmethod def from_crawler(cls, crawler): # 创建中间件实例,并将其绑定到信号 middleware = cls() crawler.signals.connect(middleware.spider_opened, signals.spider_opened) return middleware def process_request(self, request, spider): # 在这处理 response.follow() 请求 if request.callback == "parse": request = request.replace(callback=self.parse) return request def parse(self, response): # 在这处理响应数据 pass ``` 然后,你需要在 Scrapy 的设置文件中启用你的中间件: ``` # settings.py DOWNLOADER_MIDDLEWARES = { "myproject.middlewares.MyMiddleware": 500, } ``` 最后,在你的爬虫文件中使用 response.follow() 方法即可。 ### 回答2: Scrapy是一个用于爬取网站数据的Python框架,可以通过设置中间件对请求和响应进行处理。中间件是Scrapy中的一个组件,可以对请求进行修改、过滤或重定向,同时也可以对响应进行处理。 要将response.follow方法加入到中间件,需要按照以下步骤进行操作: 1. 创建一个新的Scrapy中间件类,继承自scrapy.downloadermiddlewares.DownloaderMiddleware类。 2. 在中间件类中重写process_response方法,该方法会在下载器下载完网页后被调用。 3. 在process_response方法中判断当前的响应是否需要进行重定向,如果需要重定向,则调用response.follow方法来跟进重定向的URL。 4. 返回处理后的响应对象。 下面是一个示例: ```python from scrapy import Request class MyMiddleware(object): def process_response(self, request, response, spider): if response.status in [301, 302]: redirected_url = response.headers.get('Location') if redirected_url: new_request = Request( url=redirected_url, headers=request.headers, cookies=request.cookies, meta=request.meta, dont_filter=True # 如果要进行重定向,需要设置dont_filter为True,避免重复访问 ) # 调用response.follow方法来跟进重定向的URL return spider.follow(new_request) return response ``` 在上述示例中,我们创建了一个名为MyMiddleware中间件类,其中重写了process_response方法。方法中首先判断响应的状态码是否为301或302,如果是,则获取重定向的URL,并创建一个新的Request对象。然后,通过调用spider.follow方法来跟进重定向的URL。最后,返回处理后的响应对象。 需要注意的是,上述示例仅为演示如何将response.follow方法加入到中间件,具体的实现方式可能会因具体的项目需求而有所变化。 ### 回答3: 要将`response.follow`方法添加到Scrapy的中间件中,你需要按照以下几个步骤进行操作: 1. 创建一个自定义的中间件类,继承自Scrapy的`DownloaderMiddleware`类,并重写`process_request`方法: ```python from scrapy import Request class MyMiddleware(object): def process_request(self, request, spider): if isinstance(request, Request): # 判断请求是否为Request类型 if 'follow' in request.meta: # 判断请求的meta中是否存在'follow'字段 url = request.url callback = request.callback priority = request.priority dont_filter = request.dont_filter return Request(url=url, callback=callback, priority=priority, dont_filter=dont_filter) return None ``` 2. 在Scrapy的设置文件`settings.py`中,将自定义的中间件添加到`DOWNLOADER_MIDDLEWARES`配置项中: ```python DOWNLOADER_MIDDLEWARES = { 'yourproject.middlewares.MyMiddleware': 543, # 数字越小,优先级越高 } ``` 3. 在爬虫中使用`response.follow`方法,并在请求的meta中添加`'follow': True`,以触发中间件的处理: ```python import scrapy class MySpider(scrapy.Spider): name = 'myspider' def start_requests(self): url = 'http://example.com/page1' yield scrapy.Request(url=url, callback=self.parse, meta={'follow': True}) def parse(self, response): # 使用response.follow方法,会调用中间件中的process_request方法 yield response.follow('/page2', callback=self.parse_page2) def parse_page2(self, response): # 解析爬取到的数据 pass ``` 通过以上步骤,你就可以将`response.follow`方法添加到中间件中,并在需要时使用`response.follow`方法进行请求的跟进。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值