public function run() { $this->container = $this->container ?: new Container; try { // 如果是一个控制器路由规则 // 显然我们的此条路由是一个控制器路由 if ($this->isControllerAction()) { // 将执行的结果返回给$route->run() // 跳回到上面的prepareResponse方法 return $this->runController(); } // 如果是一个闭包路由规则ControllerDispatcher return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); } } /** * Run the route action and return the response. * * @return mixed * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ protected function runController() { // return $this->controllerDispatcher()->dispatch( $this, // 通过容器解析当前路由控制器实例 $this->getController(), // 获取当前路由控制器方法 $this->getControllerMethod() ); }
Illuminate\Routing\ControllerDispatcher::dispatch方法
/** * Dispatch a request to a given controller and method. * * @param \Illuminate\Routing\Route $route * @param mixed $controller * @param string $method * @return mixed */ public function dispatch(Route $route, $controller, $method) { $parameters = $this->resolveClassMethodDependencies( $route->parametersWithoutNulls(), $controller, $method ); if (method_exists($controller, 'callAction')) { // 执行基类控制器中的callAction方法并返回执行结果 return $controller->callAction($method, $parameters); } return $controller->{$method}(...array_values($parameters)); }
控制器方法返回的结果到Router::runRouteWithinStack方法
protected function runRouteWithinStack(Route $route, Request $request) { $shouldSkipMiddleware = $this->container->bound('middleware.disable') && $this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { return $this->prepareResponse( // 返回到这里 然后执行prepareResponse方法 $request, $route->run() ); }); } // 实际调用的是toResponse方法 // 注意这里的$response是直接从控制器中返回的任何东西 public static function toResponse($request, $response) { if ($response instanceof Responsable) { // 我们当然可以直接从控制器中返回一个实现了Responsable接口的实例 $response = $response->toResponse($request); } if ($response instanceof PsrResponseInterface) { // 什么??? laravel还支持psr7?? 当然了 后面会附上使用文档 $response = (new HttpFoundationFactory)->createResponse($response); } elseif ($response instanceof Model && $response->wasRecentlyCreated) { // 知道为什么laravel允许直接返回一个模型了吗 $response = new JsonResponse($response, 201); } elseif (! $response instanceof SymfonyResponse && // 知道laravel为什么允许你直接返回数组了吗 ($response instanceof Arrayable || $response instanceof Jsonable || $response instanceof ArrayObject || $response instanceof JsonSerializable || is_array($response))) { $response = new JsonResponse($response); } elseif (! $response instanceof SymfonyResponse) { // 如果没匹配到 比如response是一个字符串,null等 直接生成响应类 // 我们从laravel的Response构造方法开始梳理 $response = new Response($response); } if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) { $response->setNotModified(); } return $response->prepare($request); }
首先我们来看直接生成laravel响应 Illuminate\Http\Response
继承了Symfony\Component\HttpFoundation\Response
// Symfony\Component\HttpFoundation\Response public function __construct($content = '', int $status = 200, array $headers = []) { // 可以看到基本什么都没做 $this->headers = new ResponseHeaderBag($headers); // 调用Illuminate\Http\Response的setContent方法 设置响应内容呗 $this->setContent($content); $this->setStatusCode($status); $this->setProtocolVersion('1.0'); } // Illuminate\Http\Response::setContent public function setContent($content) { $this->original = $content; // shouldBeJson方法将实现了特定接口的response或者是一个array的response转换为 // 并设置响应头 if ($this->shouldBeJson($content)) { $this->header('Content-Type', 'application/json'); // morphToJson方法保证最终给此响应设置的响应内容为json串 $content = $this->morphToJson($content); } elseif ($content instanceof Renderable) { $content = $content->render(); } // Symfony\Component\HttpFoundation\Response 如果最终设置的响应内容不是null或者字符串或者实现了__toString方法的类 那么跑出异常, 否则设置响应内容 parent::setContent($content); return $this; } // Symfony\Component\HttpFoundation\Response::setContent方法 public function setContent($content) { if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) { // php官方建议不要使用gettype方法获取变量的类型 throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content))); } // (string) 会触发__toString方法 如何对象允许的话 $this->content = (string) $content; return $this; }
拿到响应后执行return $response->prepare($request);