Laravel5.5源碼詳解 – Laravel-debugbar 及使用elementUI - ajax的注意事項
關於laravel對中介軟體的處理,請參中介軟體考另文,
Laravel5.5源碼詳解 – 中介軟體MiddleWare分析
這裡只是快速把debugbar的交易處理流程記錄一遍。
我在Illuminate\Pipeline\Pipeline的then函數中進行中介軟體捕獲,發現有下面這些中介軟體,
array:6 [▼ 0 => "Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode" 1 => "Illuminate\Foundation\Http\Middleware\ValidatePostSize" 2 => "App\Http\Middleware\TrimStrings" 3 => "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull" 4 => "App\Http\Middleware\TrustProxies" 5 => "Barryvdh\Debugbar\Middleware\InjectDebugbar"]array:6 [▼ 0 => "App\Http\Middleware\EncryptCookies" 1 => "Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse" 2 => "Illuminate\Session\Middleware\StartSession" 3 => "Illuminate\View\Middleware\ShareErrorsFromSession" 4 => "App\Http\Middleware\VerifyCsrfToken" 5 => "Illuminate\Routing\Middleware\SubstituteBindings"]
其中就包括這個Barryvdh\Debugbar\Middleware\InjectDebugbar,它是在larave啟動時,在vendor\composer\installed.json發現並引入,
laravel-debugbar的配置在Barryvdh\laravel-debugbar\config\debugbar,裡面解釋比較詳盡,這裡也不再重複。順便說一下,這個類是在Barryvdh\Debugbar\ServiceProvider中註冊的,
<?php namespace Barryvdh\Debugbar;use Barryvdh\Debugbar\Middleware\DebugbarEnabled;use Barryvdh\Debugbar\Middleware\InjectDebugbar;use DebugBar\DataFormatter\DataFormatter;use DebugBar\DataFormatter\DataFormatterInterface;use Illuminate\Contracts\Http\Kernel;use Illuminate\Routing\Router;use Illuminate\Session\SessionManager;class ServiceProvider extends \Illuminate\Support\ServiceProvider{ protected $defer = false; public function register() { $configPath = __DIR__ . '/../config/debugbar.php'; $this->mergeConfigFrom($configPath, 'debugbar'); $this->app->alias( DataFormatter::class, DataFormatterInterface::class ); $this->app->singleton(LaravelDebugbar::class, function () { $debugbar = new LaravelDebugbar($this->app); if ($this->app->bound(SessionManager::class)) { $sessionManager = $this->app->make(SessionManager::class); $httpDriver = new SymfonyHttpDriver($sessionManager); $debugbar->setHttpDriver($httpDriver); } return $debugbar; } ); $this->app->alias(LaravelDebugbar::class, 'debugbar'); $this->app->singleton('command.debugbar.clear', function ($app) { return new Console\ClearCommand($app['debugbar']); } ); $this->commands(['command.debugbar.clear']); } // 這裡註冊了很多事件處理功能,都是後面在處理request的時候可能會用到的 public function boot() { $configPath = __DIR__ . '/../config/debugbar.php'; $this->publishes([$configPath => $this->getConfigPath()], 'config'); $routeConfig = [ 'namespace' => 'Barryvdh\Debugbar\Controllers', 'prefix' => $this->app['config']->get('debugbar.route_prefix'), 'domain' => $this->app['config']->get('debugbar.route_domain'), 'middleware' => [DebugbarEnabled::class], ]; $this->getRouter()->group($routeConfig, function($router) { $router->get('open', [ 'uses' => 'OpenHandlerController@handle', 'as' => 'debugbar.openhandler', ]); $router->get('clockwork/{id}', [ 'uses' => 'OpenHandlerController@clockwork', 'as' => 'debugbar.clockwork', ]); $router->get('assets/stylesheets', [ 'uses' => 'AssetController@css', 'as' => 'debugbar.assets.css', ]); $router->get('assets/javascript', [ 'uses' => 'AssetController@js', 'as' => 'debugbar.assets.js', ]); }); $this->registerMiddleware(InjectDebugbar::class); } protected function getRouter() { return $this->app['router']; } protected function getConfigPath() { return config_path('debugbar.php'); } protected function publishConfig($configPath) { $this->publishes([$configPath => config_path('debugbar.php')], 'config'); } protected function registerMiddleware($middleware) { $kernel = $this->app[Kernel::class]; $kernel->pushMiddleware($middleware); } public function provides() { return ['debugbar', 'command.debugbar.clear', DataFormatterInterface::class, LaravelDebugbar::class]; }}
重點在這裡,實際處理response和request的handle函數在Barryvdh\Debugbar\Middleware\InjectDebugbar中,
<?php namespace Barryvdh\Debugbar\Middleware;use Error;use Closure;use Exception;use Illuminate\Http\Request;use Barryvdh\Debugbar\LaravelDebugbar;use Illuminate\Contracts\Container\Container;use Illuminate\Contracts\Debug\ExceptionHandler;use Symfony\Component\Debug\Exception\FatalThrowableError;class InjectDebugbar{ protected $container; protected $debugbar; protected $except = []; public function __construct(Container $container, LaravelDebugbar $debugbar) { $this->container = $container; $this->debugbar = $debugbar; $this->except = config('debugbar.except') ?: []; } public function handle($request, Closure $next) { // 如果debugbar沒有使能,或傳入的request是空的,則直接返回。 if (!$this->debugbar->isEnabled() || $this->inExceptArray($request)) { return $next($request); } // 註冊交易處理功能 $this->debugbar->boot(); try { /** @var \Illuminate\Http\Response $response */ // 可以看到,handle是處理後置的,也就是在(來回兩次經過handle)回途中處理函數, // 所以這裡先$next() $response = $next($request); } catch (Exception $e) { $response = $this->handleException($request, $e); } catch (Error $error) { $e = new FatalThrowableError($error); $response = $this->handleException($request, $e); } // 處理後置,接上面的next()之後才是debugbar幹活的時間 // Modify the response to add the Debugbar $this->debugbar->modifyResponse($request, $response); // 處理完畢,返回結果 return $response; }
上面這段,真正起作用的就是這句:$this->debugbar->modifyResponse($request, $response); ,它是debugbar 修改response的地方所在,具體請看在Barryvdh\Debugbar\LaravelDebugbar,請注意其中的注釋,
public function modifyResponse(Request $request, Response $response){ // 如果沒有使能,就直接返回response $app = $this->app; if (!$this->isEnabled() || $this->isDebugbarRequest()) { return $response; } // Show the Http Response Exception in the Debugbar, when available // 如果有Http異常,則列印顯示出來 if (isset($response->exception)) { $this->addThrowable($response->exception); } // 要不要調試設定資訊,預設是不需要的 if ($this->shouldCollect('config', false)) { try { $configCollector = new ConfigCollector(); $configCollector->setData($app['config']->all()); $this->addCollector($configCollector); } catch (\Exception $e) { $this->addThrowable( new Exception( 'Cannot add ConfigCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e ) ); } } // 如果綁定session調試 if ($this->app->bound(SessionManager::class)){ /** @var \Illuminate\Session\SessionManager $sessionManager */ $sessionManager = $app->make(SessionManager::class); $httpDriver = new SymfonyHttpDriver($sessionManager, $response); $this->setHttpDriver($httpDriver); if ($this->shouldCollect('session') && ! $this->hasCollector('session')) { try { $this->addCollector(new SessionCollector($sessionManager)); } catch (\Exception $e) { $this->addThrowable( new Exception( 'Cannot add SessionCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e ) ); } } } else { $sessionManager = null; } // 貌似這句的意思是,如果只調試一個session? 還沒進入源碼深究。 if ($this->shouldCollect('symfony_request', true) && !$this->hasCollector('request')) { try { $this->addCollector(new RequestCollector($request, $response, $sessionManager)); } catch (\Exception $e) { $this->addThrowable( new Exception( 'Cannot add SymfonyRequestCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e ) ); } } // 如果要支援Clockwork調試,(比如支援Chrome外掛程式Clockwork調試) if ($app['config']->get('debugbar.clockwork') && ! $this->hasCollector('clockwork')) { try { $this->addCollector(new ClockworkCollector($request, $response, $sessionManager)); } catch (\Exception $e) { $this->addThrowable( new Exception( 'Cannot add ClockworkCollector to Laravel Debugbar: ' . $e->getMessage(), $e->getCode(), $e ) ); } $this->addClockworkHeaders($response); } // 首先判斷一下,這是不是一個redirect()的請求(重新整理頁面) // 這個判斷的語句原型是$this->statusCode >= 300 && $this->statusCode < 400; // 函數原型在vendor\symfony\http-foundation\Response.php中, if ($response->isRedirection()) { try { $this->stackData(); } catch (\Exception $e) { $app['log']->error('Debugbar exception: ' . $e->getMessage()); } } elseif ( // 如果是ajax請求,並且已經設定了對ajax進行調試,則這在裡處理 $this->isJsonRequest($request) && $app['config']->get('debugbar.capture_ajax', true) ) { try { $this->sendDataInHeaders(true); if ($app['config']->get('debugbar.add_ajax_timing', false)) { $this->addServerTimingHeaders($response); } } catch (\Exception $e) { $app['log']->error('Debugbar exception: ' . $e->getMessage()); } } elseif ( // 如果headers有Content-Type這個標籤,並且不是html,那麼就應該是JSON資料 // 很明顯,這裡只對Content-Type=JSON的資料進行操作, // 對其他類型的資料,如圖片,MSWORD等,則直接拋出異常 ($response->headers->has('Content-Type') && strpos($response->headers->get('Content-Type'), 'html') === false) || $request->getRequestFormat() !== 'html' || $response->getContent() === false ) { try { // Just collect + store data, don't inject it. $this->collect(); } catch (\Exception $e) { $app['log']->error('Debugbar exception: ' . $e->getMessage()); } } elseif ($app['config']->get('debugbar.inject', true)) { // 對普通的情況,debugbar會在這裡修改的response,並注入渲染 try { $this->injectDebugbar($response); } catch (\Exception $e) { $app['log']->error('Debugbar exception: ' . $e->getMessage()); } } return $response; }
用到的 $app['config'] 的原貌是這樣的,
Repository {#24 ▼ #items: array:13 [▼ "app" =>