官网解释
这里的中间件指的是 “中间件模式”,该功能属于 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';
}
}
返回结果