Response前面两节我们分别讲了Laravel的controller和Request对象。在关于Request对象的部分,我们看到了Request对象是如何创建的以及它支持的方法。定义在哪里?我们在讲controller的时候,详细描述了如何找到Request对应的controller方法,然后执行handler。这一节我们会讲剩下的部分,controller方法的执行结果是如何转化为响应对象Response,然后返回给客户端的。创建响应让我们回到Laravel执行路由处理程序以返回响应的代码块:(函数()使用($route){return$route;});$this->events->dispatch(newEvents\RouteMatched($route,$request));返回$this->prepareResponse($request,$this->runRouteWithinStack($route,$request));}protectedfunctionrunRouteWithinStack(Route$route,Request$request){$shouldSkipMiddleware=$this->container->bound('middleware.disable')&&$this->container->make('middleware.disable')===真;//收集应用在路由和控制器中的中间件$middleware=$shouldSkipMiddleware?[]:$this->gatherRouteMiddleware($route);返回(新管道($this->container))->发送($request)->through($middleware)->then(函数($request)使用($route){return$this->prepareResponse($request,$route->run());});}}在控制器部分,我们已经提到runRouteWithinStack方法是路由处理程序(控制器方法或闭包处理程序)最终执行的地方。通过上面的代码,我们也可以看到执行结果会传递给Router的prepareResponse方法。当程序流程回到runRoute时,会再次执行prepareResponse方法,获取返回给客户端的Response对象。让我们仔细看看prepareResponse方法classRouterimplementsRegistrarContract,BindingRegistrar{/***通过给定值创建Response对象**@param\Symfony\Component\HttpFoundation\Request$request*@parammixed$response*@return\Illuminate\Http\Response|\Illuminate\Http\JsonResponse*/publicfunctionprepareResponse($request,$response){returnstatic::toResponse($request,$response);}}publicstaticfunctiontoResponse($request,$response){if($responseinstanceofResponsable){$response=$response->toResponse($request);}if($responseinstanceofPsrResponseInterface){$response=(newHttpFoundationFactory)->createResponse($response);}elseif(!$responseinstanceofSymfonyResponse&&($responseinstanceofArrayable||$responseinstanceofJsonable||$responseinstanceofArrayObject||$响应实例JsonSerializable||is_array($response))){$response=newJsonResponse($response);}elseif(!$responseinstanceofSymfonyResponse){$response=newResponse($response);}if($response->getStatusCode()===Response::HTTP_NOT_MODIFIED){$response->setNotModified();}返回$response->prepare($request);在上面的代码中我们看到有3种Response:ClassNameRepresentationPsrResponseInterface(PsrHttpMessageResponseInterfaceAlias)Psr规范中服务器端响应的定义SymfonyComponentHttpFoundationResponse)通过prepareResponse中的逻辑定义了Laravel中常见的非JSON响应可以看出无论路由执行结果返回什么值,都会被Laravel转换成一个Response对象,而这些对象就是SymfonyComponentHttpFoundationResponse类或其子类。从这里可以看出,Laravel的Response和Request一样,也是依赖于Symfony框架的HttpFoundation组件实现的。我们看一下SymfonyComponentHttpFoundationResponse的构造方法:新的ResponseHeaderBag($headers);$this->setContent($content);$this->setStatusCode($status);$this->setProtocolVersion('1.0');}//设置响应内容publicfunctionsetContent($content){if(null!==$content&&!is_string($content)&&!is_numeric($content)&&!is_callable(array($content,'__toString'))){thrownew\UnexpectedValueException(sprintf('响应内容必须是一个字符串或对象实现__toString(),"%s"given.',gettype($content)));}$this->content=(string)$content;返回$这个;}}所以路由处理程序Response对象的返回值将在创建Response对象时设置为该对象的content属性。该属性的值是返回给客户端的响应的响应内容。设置Responseheaders生成Response对象后,必须执行该对象的prepare方法。这个方法定义在Symfony\Component\HttpFoundation\Resposne类中。它的主要目的是对Response进行微调,使其符合HTTP/1.1协议(RFC2616)。namespaceSymfony\Component\HttpFoundation;classResponse{//在发送给客户端之前修改响应以符合HTTP/1.1协议publicfunctionprepare(Request$request){$headers=$this->headers;如果($this->isInformational()||$this->isEmpty()){$this->setContent(null);$headers->remove('内容类型');$headers->remove('Content-Length');}else{//基于请求的内容类型if(!$headers->has('Content-Type')){$format=$request->getRequestFormat();if(null!==$format&&$mimeType=$request->getMimeType($format)){$headers->set('Content-Type',$mimeType);}}//修复内容类型$charset=$this->charset?:'UTF-8';if(!$headers->has('Content-Type')){$headers->set('Content-Type','text/html;charset='.$cha复位);}elseif(0===stripos($headers->get('Content-Type'),'text/')&&false===stripos($headers->get('Content-Type'),'charset')){//添加字符集$headers->set('Content-Type',$headers->get('Content-Type').';charset='.$charset);}//修复内容长度if($headers->has('Transfer-Encoding')){$headers->remove('Content-Length');}if($request->isMethod('HEAD')){//cf.RFC261614.13$length=$headers->get('Content-Length');$this->setContent(null);如果($length){$headers->set('Content-Length',$length);}}}//修复协议if('HTTP/1.0'!=$request->server->get('SERVER_PROTOCOL')){$this->setProtocolVersion('1.1');}//检查k如果我们需要发送额外的过期信息头if('1.0'==$this->getProtocolVersion()&&false!==strpos($this->headers->get('Cache-Control'),'no-缓存')){$this->headers->set('pragma','no-cache');$this->headers->set('expires',-1);}$this->ensureIEOverSSLCompatibility($request);返回$这个;}}Prepare针对各种情况设置相应的响应头,比如Content-Type,Content-Length等,这些常用的头字段发送Response来创建和设置Response,它会流经路由和框架中间件的后期操作,在中间件的后期操作中,一般会对Response进行进一步的处理,最后程序流回到HttpKernel,HttpKernel会将Response发送给客户端。我们来看看这部分代码。//入口文件public/index.php$kernel=$app->make(Illuminate\Contracts\Http\Kernel::class);$response=$kernel->handle($request=Illuminate\Http\Request::capture());$response->send();$kernel->terminate($request,$response);命名空间Symfony\Component\HttpFoundation;classResponse{publicfunctionsend(){$this->sendHeaders();$this->sendContent();如果(function_exists('fastcgi_finish_request')){fastcgi_finish_request();}elseif('cli'!==PHP_SAPI){static::closeOutputBuffers(0,true);}返回$this;}//发送headers到客户端publicfunctionsendHeaders(){//headers已经被开发者发送if(headers_sent()){return$this;}//headersforeach($this->headers->allPreserveCaseWithoutCookies()as$name=>$values){foreach($valuesas$value){header($name.':'.$value,false,$this->statusCode);}}//状态标题(sprintf('HTTP/%s%s%s',$this->version,$this->statusCode,$this->statusText),true,$this->statusCode);//cookiesforeach($this->headers->getCookies()as$cookie){if($cookie->isRaw()){setrawcookie($cookie->getName(),$cookie->getValue(),$cookie->getExpiresTime(),$cookie->getPath(),$cookie->getDomain(),$cookie->isSecure(),$cookie->isHttpOnly());}else{setcookie($cookie->getName(),$cookie->getValue(),$cookie->getExpiresTime(),$cookie->getPath(),$cookie->getDomain(),$cookie->isSecure(),$cookie->isHttpOnly());}}返回$this;}//将响应内容发送给客户端publicfunctionsendContent(){echo$this->content;返回$这个;}}send的逻辑很好理解,之前设置好的headerss设置为HTTP响应的头域,Content会被回显,然后设置为HTTP响应的主体。最后,PHP会将完整的HTTP响应发送给客户端。发送响应后,HttpKernel会执行terminate方法调用terminate中间件中的terminate方法,最终执行应用的terminate方法结束整个应用生命周期(从收到请求到返回响应)。本文已收录在Laravel源码学习系列文章中,欢迎访问阅读。
