這篇文章主要介紹了關於Laravel核心解讀Request,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
Request
很多架構都會將來自用戶端的請求抽象成類方便應用程式使用,在Laravel中也不例外。Illuminate\Http\Request
類在Laravel架構中就是對用戶端請求的抽象,它是構建在Symfony
架構提供的Request組件基礎之上的。今天這篇文章就簡單來看看Laravel是怎麼建立請求Request對象的,而關於Request對象為應用提供的能力我並不會過多去說,在我講完建立過程後你也就知道去源碼哪裡找Request對象提供的方法了,網上有些速查表列舉了一些Request提供的方法不過不夠全並且有的也沒有解釋,所以我還是推薦在開發中如果好奇Request是否已經實現了你想要的能力時去Request的源碼裡看下有沒有提供對應的方法,方法注釋裡都清楚地標明了每個方法的執行結果。下面讓我們進入正題吧。
建立Request對象
我們可以在Laravel應用程式的index.php
檔案中看到,在Laravel應用程式正式啟動完成前Request對象就已經被建立好了:
//public/index.php$app = require_once __DIR__.'/../bootstrap/app.php';$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle( //建立request對象 $request = Illuminate\Http\Request::capture());
用戶端的HTTP請求是Illuminate\Http\Request
類的對象
class Request extends SymfonyRequest implements Arrayable, ArrayAccess{ //建立Request執行個體 public static function capture() { static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals()); }}
通過Illuminate\Http\Request
類的源碼可以看到它是繼承自Symfony Request
類的,所以Illuminate\Http\Request
類中實現的很多功能都是以Symfony Reques
提供的功能為基礎來實現的。上面的代碼就可以看到capture
方法建立Request對象時也是依賴於Symfony Request
類的執行個體的。
namespace Symfony\Component\HttpFoundation;class Request{ /** * 根據PHP提供的超級全域數組來建立Smyfony Request執行個體 * * @return static */ public static function createFromGlobals() { // With the php's bug #66606, the php's built-in web server // stores the Content-Type and Content-Length header values in // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields. $server = $_SERVER; if ('cli-server' === PHP_SAPI) { if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) { $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH']; } if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) { $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE']; } } $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server); if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) ) { parse_str($request->getContent(), $data); $request->request = new ParameterBag($data); } return $request; } }
上面的代碼有一處需要額外解釋一下,自PHP5.4開始PHP內建的builtin web server可以通過命令列解譯器來啟動,例如:
php -S localhost:8000 -t htdocs
-S <addr>:<port> Run with built-in web server.-t <docroot> Specify document root <docroot> for built-in web server.
但是內建web server有一個bug是將CONTENT_LENGTH
和CONTENT_TYPE
這兩個請求首部儲存到了HTTP_CONTENT_LENGTH
和HTTP_CONTENT_TYPE
中,為了統一內建伺服器和真正的server中的請求首部欄位所以在這裡做了特殊處理。
Symfony Request 執行個體的建立是通過PHP中的超級全域數組來建立的,這些超級全域數組有$_GET
,$_POST
,$_COOKIE
,$_FILES
,$_SERVER
涵蓋了PHP中所有與HTTP請求相關的超級全域數組,建立Symfony Request執行個體時會根據這些全域數組建立Symfony Package裡提供的ParamterBag
ServerBag
FileBag
HeaderBag
執行個體,這些Bag都是Symfony提供地針對不同HTTP組成部分的訪問和設定API, 關於Symfony提供的ParamterBag
這些執行個體有興趣的讀者自己去源碼裡看看吧,這裡就不多說了。
class Request{ /** * @param array $query The GET parameters * @param array $request The POST parameters * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) * @param array $cookies The COOKIE parameters * @param array $files The FILES parameters * @param array $server The SERVER parameters * @param string|resource|null $content The raw body data */ public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); } public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { $this->request = new ParameterBag($request); $this->query = new ParameterBag($query); $this->attributes = new ParameterBag($attributes); $this->cookies = new ParameterBag($cookies); $this->files = new FileBag($files); $this->server = new ServerBag($server); $this->headers = new HeaderBag($this->server->getHeaders()); $this->content = $content; $this->languages = null; $this->charsets = null; $this->encodings = null; $this->acceptableContentTypes = null; $this->pathInfo = null; $this->requestUri = null; $this->baseUrl = null; $this->basePath = null; $this->method = null; $this->format = null; } }
可以看到Symfony Request類除了上邊說到的那幾個,還有很多屬性,這些屬性在一起構成了對HTTP請求完整的抽象,我們可以通過執行個體屬性方便地訪問Method
,Charset
等這些HTTP請求的屬性。
拿到Symfony Request執行個體後, Laravel會複製這個執行個體並重設其中的一些屬性:
namespace Illuminate\Http;class Request extends ....{ //在Symfony request instance的基礎上建立Request執行個體 public static function createFromBase(SymfonyRequest $request) { if ($request instanceof static) { return $request; } $content = $request->content; $request = (new static)->duplicate( $request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all() ); $request->content = $content; $request->request = $request->getInputSource(); return $request; } public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) { return parent::duplicate($query, $request, $attributes, $cookies, $this->filterFiles($files), $server); }} //Symfony Request中的 duplicate方法 public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) { $dup = clone $this; if (null !== $query) { $dup->query = new ParameterBag($query); } if (null !== $request) { $dup->request = new ParameterBag($request); } if (null !== $attributes) { $dup->attributes = new ParameterBag($attributes); } if (null !== $cookies) { $dup->cookies = new ParameterBag($cookies); } if (null !== $files) { $dup->files = new FileBag($files); } if (null !== $server) { $dup->server = new ServerBag($server); $dup->headers = new HeaderBag($dup->server->getHeaders()); } $dup->languages = null; $dup->charsets = null; $dup->encodings = null; $dup->acceptableContentTypes = null; $dup->pathInfo = null; $dup->requestUri = null; $dup->baseUrl = null; $dup->basePath = null; $dup->method = null; $dup->format = null; if (!$dup->get('_format') && $this->get('_format')) { $dup->attributes->set('_format', $this->get('_format')); } if (!$dup->getRequestFormat(null)) { $dup->setRequestFormat($this->getRequestFormat(null)); } return $dup; }
Request對象建立好後在Laravel應用中我們就能方便的應用它提供的能力了,在使用Request對象時如果你不知道它是否實現了你想要的功能,很簡單直接去Illuminate\Http\Request
的源碼檔案裡查看就好了,所有方法都列在了這個源碼檔案裡,比如:
/** * Get the full URL for the request. * 擷取請求的URL(包含host, 不包括query string) * * @return string */public function fullUrl(){ $query = $this->getQueryString(); $question = $this->getBaseUrl().$this->getPathInfo() == '/' ? '/?' : '?'; return $query ? $this->url().$question.$query : $this->url();}/** * Get the full URL for the request with the added query string parameters. * 擷取包括了query string 的完整URL * * @param array $query * @return string */public function fullUrlWithQuery(array $query){ $question = $this->getBaseUrl().$this->getPathInfo() == '/' ? '/?' : '?'; return count($this->query()) > 0 ? $this->url().$question.http_build_query(array_merge($this->query(), $query)) : $this->fullUrl().$question.http_build_query($query);}
Request經過的驛站
建立完Request對象後, Laravel的Http Kernel會接著往下執行:載入服務提供器引導Laravel應用、啟動應用、讓Request經過基礎的中介軟體、通過Router匹配尋找Request對應的路由、執行匹配到的路由、Request經過路由上到中介軟體到達控制器方法。
總結
隨著Request最終到達對應的控制器方法後它的使命基本上也就完成了, 在控制器方法裡從Request中擷取輸入參數然後執行應用的某一商務邏輯獲得結果,結果會被轉化成Response響應對象返回給發起請求的用戶端。
這篇文章主要梳理了Laravel中Request對象,主要是想讓大家知道如何去尋找Laravel中Request現有提供了哪些能力供我們使用避免我們在業務代碼裡重新造輪子去實現Request已經提供的方法。
以上就是本文的全部內容,希望對大家的學習有所協助,更多相關內容請關注topic.alibabacloud.com!