HTTP 請求
簡介
Laravel 的 Illuminate\Http\Request
類別提供了一個物件導向的方式來與應用程式正在處理的目前 HTTP 請求互動,並檢索請求提交的輸入、Cookie 和檔案。
與請求互動
存取請求
若要透過依賴注入取得目前 HTTP 請求的實例,您應該在您的路由閉包或控制器方法中類型提示 Illuminate\Http\Request
類別。傳入的請求實例將由 Laravel 服務容器 自動注入。
<?php namespace App\Http\Controllers; use Illuminate\Http\RedirectResponse;use Illuminate\Http\Request; class UserController extends Controller{ /** * Store a new user. */ public function store(Request $request): RedirectResponse { $name = $request->input('name'); // Store the user... return redirect('/users'); }}
如上所述,您也可以在路由閉包上類型提示 Illuminate\Http\Request
類別。當執行時,服務容器會自動將傳入的請求注入到閉包中。
use Illuminate\Http\Request; Route::get('/', function (Request $request) { // ...});
依賴注入和路由參數
如果您的控制器方法也期望從路由參數取得輸入,您應該在其他依賴項之後列出您的路由參數。例如,如果您的路由定義如下
use App\Http\Controllers\UserController; Route::put('/user/{id}', [UserController::class, 'update']);
您仍然可以類型提示 Illuminate\Http\Request
並透過定義您的控制器方法如下來存取您的 id
路由參數
<?php namespace App\Http\Controllers; use Illuminate\Http\RedirectResponse;use Illuminate\Http\Request; class UserController extends Controller{ /** * Update the specified user. */ public function update(Request $request, string $id): RedirectResponse { // Update the user... return redirect('/users'); }}
請求路徑、主機和方法
Illuminate\Http\Request
實例提供了各種方法來檢查傳入的 HTTP 請求,並擴展了 Symfony\Component\HttpFoundation\Request
類別。我們將在下面討論一些最重要的的方法。
檢索請求路徑
path
方法會傳回請求的路徑資訊。因此,如果傳入的請求目標是 http://example.com/foo/bar
,則 path
方法會傳回 foo/bar
。
$uri = $request->path();
檢查請求路徑 / 路由
is
方法可讓您驗證傳入的請求路徑是否符合給定的模式。使用此方法時,您可以使用 *
字元作為萬用字元。
if ($request->is('admin/*')) { // ...}
使用 routeIs
方法,您可以判斷傳入的請求是否符合具名路由。
if ($request->routeIs('admin.*')) { // ...}
檢索請求 URL
若要檢索傳入請求的完整 URL,您可以使用 url
或 fullUrl
方法。url
方法將傳回不帶查詢字串的 URL,而 fullUrl
方法則包含查詢字串。
$url = $request->url(); $urlWithQueryString = $request->fullUrl();
如果您想要將查詢字串資料附加到目前的 URL,您可以呼叫 fullUrlWithQuery
方法。此方法會將給定的查詢字串變數陣列與目前的查詢字串合併。
$request->fullUrlWithQuery(['type' => 'phone']);
如果您想要取得目前的 URL 而沒有給定的查詢字串參數,您可以使用 fullUrlWithoutQuery
方法。
$request->fullUrlWithoutQuery(['type']);
檢索請求主機
您可以透過 host
、httpHost
和 schemeAndHttpHost
方法檢索傳入請求的「主機」。
$request->host();$request->httpHost();$request->schemeAndHttpHost();
檢索請求方法
method
方法將傳回請求的 HTTP 動詞。您可以使用 isMethod
方法來驗證 HTTP 動詞是否符合給定的字串。
$method = $request->method(); if ($request->isMethod('post')) { // ...}
請求標頭
您可以使用 header
方法從 Illuminate\Http\Request
實例中檢索請求標頭。如果請求上不存在標頭,則會傳回 null
。但是,header
方法接受一個可選的第二個引數,如果請求上不存在標頭,則會傳回該引數。
$value = $request->header('X-Header-Name'); $value = $request->header('X-Header-Name', 'default');
hasHeader
方法可用於判斷請求是否包含給定的標頭。
if ($request->hasHeader('X-Header-Name')) { // ...}
為了方便起見,可以使用 bearerToken
方法從 Authorization
標頭中檢索 bearer token。如果不存在此類標頭,則會傳回空字串。
$token = $request->bearerToken();
請求 IP 位址
ip
方法可用於檢索向您的應用程式發出請求的用戶端的 IP 位址。
$ipAddress = $request->ip();
如果您想要檢索 IP 位址的陣列,包括由 Proxy 轉發的所有用戶端 IP 位址,您可以使用 ips
方法。「原始」用戶端 IP 位址將位於陣列的末尾。
$ipAddresses = $request->ips();
一般來說,IP 位址應被視為不受信任的、使用者控制的輸入,並且僅用於資訊目的。
內容協商
Laravel 提供了幾種方法,透過 Accept
標頭來檢查傳入請求所請求的內容類型。首先,getAcceptableContentTypes
方法將傳回一個陣列,其中包含請求接受的所有內容類型。
$contentTypes = $request->getAcceptableContentTypes();
accepts
方法接受一個內容類型陣列,如果請求接受任何內容類型,則傳回 true
。否則,將傳回 false
。
if ($request->accepts(['text/html', 'application/json'])) { // ...}
您可以使用 prefers
方法來判斷請求最偏好給定內容類型陣列中的哪個內容類型。如果請求不接受任何提供的內容類型,則會傳回 null
。
$preferred = $request->prefers(['text/html', 'application/json']);
由於許多應用程式僅提供 HTML 或 JSON,您可以使用 expectsJson
方法快速判斷傳入的請求是否期望 JSON 回應。
if ($request->expectsJson()) { // ...}
PSR-7 請求
PSR-7 標準 指定了 HTTP 訊息的介面,包括請求和回應。如果您想要取得 PSR-7 請求的實例而不是 Laravel 請求,您首先需要安裝一些程式庫。Laravel 使用Symfony HTTP Message Bridge元件將典型的 Laravel 請求和回應轉換為與 PSR-7 相容的實作。
composer require symfony/psr-http-message-bridgecomposer require nyholm/psr7
安裝這些程式庫後,您可以透過在路由閉包或控制器方法上類型提示請求介面來取得 PSR-7 請求。
use Psr\Http\Message\ServerRequestInterface; Route::get('/', function (ServerRequestInterface $request) { // ...});
如果您從路由或控制器傳回 PSR-7 回應實例,它將會自動轉換回 Laravel 回應實例,並由框架顯示。
輸入
檢索輸入
檢索所有輸入資料
您可以使用 all
方法以 array
形式檢索所有傳入請求的輸入資料。無論傳入的請求是來自 HTML 表單還是 XHR 請求,都可以使用此方法。
$input = $request->all();
使用 collect
方法,您可以將所有傳入請求的輸入資料檢索為 集合。
$input = $request->collect();
collect
方法也允許您將傳入請求輸入的子集檢索為集合。
$request->collect('users')->each(function (string $user) { // ...});
檢索輸入值
使用一些簡單的方法,您可以從您的 Illuminate\Http\Request
實例存取所有使用者輸入,而無需擔心請求使用了哪個 HTTP 動詞。無論 HTTP 動詞為何,都可以使用 input
方法來檢索使用者輸入。
$name = $request->input('name');
您可以將預設值作為 input
方法的第二個引數傳遞。如果請求上不存在請求的輸入值,則會傳回此值。
$name = $request->input('name', 'Sally');
當使用包含陣列輸入的表單時,請使用「點」表示法來存取陣列。
$name = $request->input('products.0.name'); $names = $request->input('products.*.name');
您可以呼叫不帶任何引數的 input
方法,以檢索所有輸入值作為關聯陣列。
$input = $request->input();
從查詢字串檢索輸入
雖然 input
方法會從整個請求酬載(包含查詢字串)中檢索值,但 query
方法只會從查詢字串中檢索值。
$name = $request->query('name');
如果請求的查詢字串值資料不存在,將會返回此方法的第二個參數。
$name = $request->query('name', 'Helen');
您可以不帶任何參數呼叫 query
方法,以檢索所有查詢字串值作為關聯陣列。
$query = $request->query();
檢索 JSON 輸入值
當將 JSON 請求傳送到您的應用程式時,只要請求的 Content-Type
標頭正確設定為 application/json
,您就可以透過 input
方法存取 JSON 資料。您甚至可以使用「點」語法來檢索巢狀於 JSON 陣列/物件中的值。
$name = $request->input('user.name');
檢索可字串化的輸入值
您可以不用將請求的輸入資料作為原始的 string
檢索,而是可以使用 string
方法將請求資料檢索為 Illuminate\Support\Stringable
的實例。
$name = $request->string('name')->trim();
檢索整數輸入值
要以整數檢索輸入值,您可以使用 integer
方法。此方法會嘗試將輸入值轉換為整數。如果輸入不存在或轉換失敗,它將返回您指定的預設值。這對於分頁或其他數字輸入特別有用。
$perPage = $request->integer('per_page');
檢索布林輸入值
在處理像核取方塊這樣的 HTML 元素時,您的應用程式可能會收到實際上是字串的「真值」。例如,「true」或「on」。為了方便起見,您可以使用 boolean
方法將這些值檢索為布林值。對於 1、「1」、true、「true」、「on」和「yes」,boolean
方法會返回 true
。所有其他值都將返回 false
。
$archived = $request->boolean('archived');
檢索日期輸入值
為了方便起見,包含日期/時間的輸入值可以使用 date
方法檢索為 Carbon 實例。如果請求不包含具有指定名稱的輸入值,則會返回 null
。
$birthday = $request->date('birthday');
date
方法接受的第二和第三個參數可用於指定日期的格式和時區。
$elapsed = $request->date('elapsed', '!H:i', 'Europe/Madrid');
如果輸入值存在但格式無效,則會拋出 InvalidArgumentException
;因此,建議您在調用 date
方法之前驗證輸入。
檢索列舉輸入值
對應於 PHP 列舉的輸入值也可以從請求中檢索。如果請求不包含具有指定名稱的輸入值,或者列舉沒有與輸入值匹配的後備值,則會返回 null
。 enum
方法接受輸入值的名稱和列舉類別作為其第一個和第二個參數。
use App\Enums\Status; $status = $request->enum('status', Status::class);
如果輸入值是與 PHP 列舉對應的值的陣列,您可以使用 enums
方法將值陣列檢索為列舉實例。
use App\Enums\Product; $products = $request->enums('products', Product::class);
透過動態屬性檢索輸入
您也可以使用 Illuminate\Http\Request
實例上的動態屬性來存取使用者輸入。例如,如果您的應用程式的其中一個表單包含一個 name
欄位,您可以像這樣存取該欄位的值:
$name = $request->name;
當使用動態屬性時,Laravel 會首先在請求酬載中尋找參數的值。如果不存在,Laravel 會在匹配路由的參數中尋找該欄位。
檢索部分輸入資料
如果您需要檢索輸入資料的子集,您可以使用 only
和 except
方法。這兩個方法都接受單個 array
或動態參數列表。
$input = $request->only(['username', 'password']); $input = $request->only('username', 'password'); $input = $request->except(['credit_card']); $input = $request->except('credit_card');
only
方法會返回您請求的所有鍵/值對;但是,它不會返回請求中不存在的鍵/值對。
輸入存在
您可以使用 has
方法來判斷值是否存在於請求中。如果該值存在於請求中,has
方法會返回 true
。
if ($request->has('name')) { // ...}
當給定一個陣列時,has
方法會判斷所有指定的值是否存在。
if ($request->has(['name', 'email'])) { // ...}
如果任何指定的值存在,hasAny
方法會返回 true
。
if ($request->hasAny(['name', 'email'])) { // ...}
如果值存在於請求中,whenHas
方法會執行給定的閉包。
$request->whenHas('name', function (string $input) { // ...});
可以將第二個閉包傳遞給 whenHas
方法,如果請求中不存在指定的值,則會執行該閉包。
$request->whenHas('name', function (string $input) { // The "name" value is present...}, function () { // The "name" value is not present...});
如果您想判斷值是否存在於請求中且不是空字串,您可以使用 filled
方法。
if ($request->filled('name')) { // ...}
如果您想判斷值在請求中遺失或為空字串,您可以使用 isNotFilled
方法。
if ($request->isNotFilled('name')) { // ...}
當給定一個陣列時,isNotFilled
方法會判斷所有指定的值是否遺失或為空。
if ($request->isNotFilled(['name', 'email'])) { // ...}
如果任何指定的值不是空字串,anyFilled
方法會返回 true
。
if ($request->anyFilled(['name', 'email'])) { // ...}
如果值存在於請求中且不是空字串,whenFilled
方法會執行給定的閉包。
$request->whenFilled('name', function (string $input) { // ...});
可以將第二個閉包傳遞給 whenFilled
方法,如果指定的值未「填寫」,則會執行該閉包。
$request->whenFilled('name', function (string $input) { // The "name" value is filled...}, function () { // The "name" value is not filled...});
要判斷給定的鍵是否不存在於請求中,您可以使用 missing
和 whenMissing
方法。
if ($request->missing('name')) { // ...} $request->whenMissing('name', function () { // The "name" value is missing...}, function () { // The "name" value is present...});
合併其他輸入
有時您可能需要手動將其他輸入合併到請求的現有輸入資料中。為了實現這一點,您可以使用 merge
方法。如果給定的輸入鍵已經存在於請求中,它將被提供給 merge
方法的資料覆寫。
$request->merge(['votes' => 0]);
如果對應的鍵在請求的輸入資料中尚不存在,則可以使用 mergeIfMissing
方法將輸入合併到請求中。
$request->mergeIfMissing(['votes' => 0]);
舊輸入
Laravel 允許您在下一個請求期間保留一個請求的輸入。此功能對於在偵測到驗證錯誤後重新填寫表單特別有用。但是,如果您正在使用 Laravel 包含的 驗證功能,您可能不需要直接手動使用這些會話輸入快閃方法,因為 Laravel 的一些內建驗證機制會自動呼叫它們。
將輸入快閃到會話
Illuminate\Http\Request
類別上的 flash
方法會將目前的輸入快閃到會話,以便在使用者下一次向應用程式發出請求時可用。
$request->flash();
您也可以使用 flashOnly
和 flashExcept
方法將請求資料的子集快閃到會話。這些方法對於將密碼等敏感資訊保留在會話之外很有用。
$request->flashOnly(['username', 'email']); $request->flashExcept('password');
快閃輸入然後重新導向
由於您經常會想將輸入快閃到會話,然後重新導向到上一頁,因此您可以輕鬆地使用 withInput
方法將輸入快閃鏈接到重新導向。
return redirect('/form')->withInput(); return redirect()->route('user.create')->withInput(); return redirect('/form')->withInput( $request->except('password'));
檢索舊的輸入
要從上一個請求中檢索快閃輸入,請在 Illuminate\Http\Request
的實例上調用 old
方法。 old
方法會從 會話中提取先前快閃的輸入資料。
$username = $request->old('username');
Laravel 還提供了一個全域的 old
輔助函數。如果您在 Blade 範本中顯示舊的輸入,則使用 old
輔助函數來重新填寫表單會更方便。如果給定欄位不存在舊的輸入,則會返回 null
。
<input type="text" name="username" value="{{ old('username') }}">
Cookie
從請求中檢索 Cookie
Laravel 框架建立的所有 Cookie 都會使用驗證碼進行加密和簽名,這表示如果用戶端已變更它們,則會被視為無效。若要從請求中檢索 Cookie 值,請使用 Illuminate\Http\Request
實例上的 cookie
方法。
$value = $request->cookie('name');
輸入修剪和正規化
依預設,Laravel 會在應用程式的全域中介軟體堆疊中包含 Illuminate\Foundation\Http\Middleware\TrimStrings
和 Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull
中介軟體。這些中介軟體會自動修剪請求上的所有傳入字串欄位,並將任何空字串欄位轉換為 null
。這讓您不必擔心路由和控制器中的這些正規化問題。
停用輸入正規化
如果您想為所有請求停用此行為,您可以透過在應用程式的 bootstrap/app.php
檔案中調用 $middleware->remove
方法,從應用程式的中介軟體堆疊中移除這兩個中介軟體。
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;use Illuminate\Foundation\Http\Middleware\TrimStrings; ->withMiddleware(function (Middleware $middleware) { $middleware->remove([ ConvertEmptyStringsToNull::class, TrimStrings::class, ]);})
如果您想為應用程式的部分請求停用字串修剪和空字串轉換,您可以在應用程式的 bootstrap/app.php
檔案中使用 trimStrings
和 convertEmptyStringsToNull
中介軟體方法。這兩個方法都接受一個閉包陣列,這些閉包應該返回 true
或 false
來指示是否應跳過輸入正規化。
->withMiddleware(function (Middleware $middleware) { $middleware->convertEmptyStringsToNull(except: [ fn (Request $request) => $request->is('admin/*'), ]); $middleware->trimStrings(except: [ fn (Request $request) => $request->is('admin/*'), ]);})
檔案
檢索上傳的檔案
您可以使用 file
方法或使用動態屬性,從 Illuminate\Http\Request
實例中檢索上傳的檔案。 file
方法會返回 Illuminate\Http\UploadedFile
類別的實例,該類別會擴展 PHP SplFileInfo
類別,並提供各種與檔案互動的方法。
$file = $request->file('photo'); $file = $request->photo;
您可以使用 hasFile
方法來判斷檔案是否存在於請求中。
if ($request->hasFile('photo')) { // ...}
驗證成功的上傳
除了檢查檔案是否存在之外,您還可以透過 isValid
方法驗證上傳檔案時沒有問題。
if ($request->file('photo')->isValid()) { // ...}
檔案路徑和副檔名
UploadedFile
類別還包含用於存取檔案完整限定路徑及其副檔名的方法。 extension
方法會嘗試根據檔案的內容猜測檔案的副檔名。此副檔名可能與用戶端提供的副檔名不同。
$path = $request->photo->path(); $extension = $request->photo->extension();
其他檔案方法
UploadedFile
實例上還有各種其他可用方法。請參閱該類別的 API 文件,以取得有關這些方法的更多資訊。
儲存上傳的檔案
若要儲存上傳的檔案,您通常會使用其中一個已設定的檔案系統。 UploadedFile
類別有一個 store
方法,該方法會將上傳的檔案移動到您的其中一個磁碟,該磁碟可以是您本機檔案系統上的位置或像 Amazon S3 這樣的雲端儲存位置。
store
方法接受檔案應儲存的路徑,此路徑是相對於檔案系統設定的根目錄而言。此路徑不應包含檔名,因為系統會自動產生一個唯一的 ID 作為檔名。
store
方法也接受一個可選的第二個參數,用於指定應儲存檔案的磁碟名稱。該方法將返回檔案相對於磁碟根目錄的路徑。
$path = $request->photo->store('images'); $path = $request->photo->store('images', 's3');
如果您不希望自動產生檔名,可以使用 storeAs
方法,該方法接受路徑、檔名和磁碟名稱作為其參數。
$path = $request->photo->storeAs('images', 'filename.jpg'); $path = $request->photo->storeAs('images', 'filename.jpg', 's3');
有關 Laravel 中檔案儲存的更多資訊,請查看完整的檔案儲存文件。
設定信任的 Proxy
當您的應用程式在終止 TLS / SSL 憑證的負載平衡器後方執行時,您可能會注意到在使用 url
輔助函數時,您的應用程式有時不會產生 HTTPS 連結。這通常是因為您的應用程式正在從負載平衡器的 80 連接埠轉發流量,並且不知道它應該產生安全的連結。
要解決此問題,您可以啟用 Laravel 應用程式中包含的 Illuminate\Http\Middleware\TrustProxies
中介層,這使您可以快速自訂應用程式應信任的負載平衡器或 Proxy。您受信任的 Proxy 應使用應用程式 bootstrap/app.php
檔案中的 trustProxies
中介層方法指定。
->withMiddleware(function (Middleware $middleware) { $middleware->trustProxies(at: [ '192.168.1.1', '10.0.0.0/8', ]);})
除了配置受信任的 Proxy 外,您還可以配置應信任的 Proxy 標頭。
->withMiddleware(function (Middleware $middleware) { $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB );})
如果您正在使用 AWS Elastic Load Balancing,則 headers
值應為 Request::HEADER_X_FORWARDED_AWS_ELB
。如果您的負載平衡器使用來自 RFC 7239 的標準 Forwarded
標頭,則 headers
值應為 Request::HEADER_FORWARDED
。有關可在 headers
值中使用的常數的更多資訊,請查看 Symfony 關於信任 Proxy 的文件。
信任所有 Proxy
如果您正在使用 Amazon AWS 或其他「雲端」負載平衡器供應商,您可能不知道實際平衡器的 IP 位址。在這種情況下,您可以使用 *
來信任所有 Proxy。
->withMiddleware(function (Middleware $middleware) { $middleware->trustProxies(at: '*');})
設定信任的主機
預設情況下,Laravel 將回應它收到的所有請求,無論 HTTP 請求的 Host
標頭的內容如何。此外,在 Web 請求期間生成應用程式的絕對 URL 時,將使用 Host
標頭的值。
通常,您應該配置您的 Web 伺服器(例如 Nginx 或 Apache),僅將符合給定主機名稱的請求發送到您的應用程式。但是,如果您無法直接自訂 Web 伺服器,並且需要指示 Laravel 僅回應某些主機名稱,則可以為您的應用程式啟用 Illuminate\Http\Middleware\TrustHosts
中介層。
要啟用 TrustHosts
中介層,您應該在應用程式的 bootstrap/app.php
檔案中調用 trustHosts
中介層方法。使用此方法的 at
參數,您可以指定您的應用程式應回應的主機名稱。具有其他 Host
標頭的傳入請求將被拒絕。
->withMiddleware(function (Middleware $middleware) { $middleware->trustHosts(at: ['laravel.test']);})
預設情況下,來自應用程式 URL 子網域的請求也會自動被信任。如果您想禁用此行為,可以使用 subdomains
參數。
->withMiddleware(function (Middleware $middleware) { $middleware->trustHosts(at: ['laravel.test'], subdomains: false);})
如果您需要存取應用程式的組態檔案或資料庫以確定受信任的主機,您可以為 at
參數提供一個閉包。
->withMiddleware(function (Middleware $middleware) { $middleware->trustHosts(at: fn () => config('app.trusted_hosts'));})