追了一下发现其实github上已经有人提过这个问题了。这里我只是记录一下位置和大概说说中间件的原理。
原理篇 :关键词:pipeline 网上很多原理分析,可以了解下。
dingo关键目录vendor\dingo\api\src\Routing\Adapter\Lumen.php line 218
/**
* Remove the global application middleware as it's run from this packages
* Request middleware. Lumen runs middleware later in its life cycle
* which results in some middleware being executed twice.
*
* @return void
*/
protected function removeMiddlewareFromApp()
{
if ($this->middlewareRemoved) {
return;
}
$this->middlewareRemoved = true;
$reflection = new ReflectionClass($this->app);
$property = $reflection->getProperty('middleware');
$property->setAccessible(true);
$property->setValue($this->app, []);
$property->setAccessible(false);
}
这个方法是为了过滤已经执行过的中间件的,直接粗暴的把中间件都过滤掉了
我现在把它改成
/**
* Remove the global application middleware as it's run from this packages
* Request middleware. Lumen runs middleware later in its life cycle
* which results in some middleware being executed twice.
*
* @return void
*/
protected function removeMiddlewareFromApp()
{
if ($this->middlewareRemoved) {
return;
}
$this->middlewareRemoved = true;
$reflection = new ReflectionClass($this->app);
$property = $reflection->getProperty('middleware');
$property->setAccessible(true);
$oldMiddlewares = $property->getValue($this->app);
$newMiddlewares = [];
foreach ($oldMiddlewares as $middle) {
if ((new ReflectionClass($middle))->hasMethod('terminate') && $middle != 'Dingo\Api\Http\Middleware\Request') {
$newMiddlewares = array_merge($newMiddlewares, [$middle]);
}
}
$property->setValue($this->app, $newMiddlewares);
$property->setAccessible(false);
}
改了这里的话还是会有执行2次的问题,这个执行两次的问题原因在于,dingo和lumen都有去执行pipeline方法
在dingo中的方法在:\vendor\dingo\api\src\Http\Middleware\Request.php line121 这个方法的主要作用就是把request发送到dingo的router中
/**
* Send the request through the Dingo router.
*
* @param \Dingo\Api\Http\Request $request
*
* @return \Dingo\Api\Http\Response
*/
protected function sendRequestThroughRouter(HttpRequest $request)
{
$this->app->instance('request', $request);
return (new Pipeline($this->app))->send($request)->through($this->middleware)->then(function ($request) {
return $this->router->dispatch($request);
});
}
在lumen中,是在这个trait目录下:\vendor\laravel\lumen-framework\src\Concerns\RoutesRequests.php
/**
* Dispatch the incoming request.
*
* @param SymfonyRequest|null $request
* @return Response
*/
public function dispatch($request = null)
{
list($method, $pathInfo) = $this->parseIncomingRequest($request);
try {
return $this->sendThroughPipeline($this->middleware, function () use ($method, $pathInfo) {
if (isset($this->routes[$method.$pathInfo])) {
return $this->handleFoundRoute([true, $this->routes[$method.$pathInfo]['action'], []]);
}
return $this->handleDispatcherResponse(
$this->createDispatcher()->dispatch($method, $pathInfo)
);
});
} catch (Exception $e) {
return $this->prepareResponse($this->sendExceptionToHandler($e));
} catch (Throwable $e) {
return $this->prepareResponse($this->sendExceptionToHandler($e));
}
}
/**
* Send the request through the pipeline with the given callback.
*
* @param array $middleware
* @param \Closure $then
* @return mixed
*/
protected function sendThroughPipeline(array $middleware, Closure $then)
{
if (count($middleware) > 0 && ! $this->shouldSkipMiddleware()) {
return (new Pipeline($this))
->send($this->make('request'))
->through($middleware)
->then($then);
}
return $then();
}
既然如此,dingo这里又重复的执行了一次中间件,不如直接在dingo中去掉->through($middleware)
综上所述,我们要改动dingo源码的两个地方,一就是上面我改的那个地方,二就是\vendor\dingo\api\src\Http\Middleware\Request.php line121 方法中去掉 ->through($middleware)