HTTP 請求
📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本
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
{
/**
* 儲存一個新的使用者。
*/
public function store(Request $request): RedirectResponse
{
$name = $request->input('name');
// 儲存使用者...
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
{
/**
* 更新指定的使用者。
*/
public function update(Request $request, string $id): RedirectResponse
{
// 更新使用者...
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 方法來檢索傳入請求的「主機」:
// http://localhost:8000
$request->host(); // localhost
$request->httpHost(); // localhost:8000
$request->schemeAndHttpHost(); // http://localhost:8000
檢索請求方法
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 = $request->bearerToken();
請求 IP 位址
ip 方法可用於檢索向您的應用程式發出請求的客戶端的 IP 位址:
$ipAddress = $request->ip();
如果您希望檢索一個 IP 位址陣列,包括由代理轉發的所有客戶端 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()) {
// ...
}
如果您需要判斷請求是特別首選 Markdown 還是接受 Markdown 以及其他內容類型(例如在為 AI 代理或其他消費 Markdown 回應的客戶端提供服務時),可以使用 wantsMarkdown 和 acceptsMarkdown 方法:
if ($request->wantsMarkdown()) {
// 客戶端最首選的內容類型是 text/markdown...
}
if ($request->acceptsMarkdown()) {
// 客戶端接受 Markdown 回應...
}
PSR-7 請求
PSR-7 標準指定了 HTTP 訊息的介面,包括請求和回應。如果您希望取得 PSR-7 請求的實例而不是 Laravel 請求,您首先需要安裝一些函式庫。Laravel 使用 Symfony HTTP Message Bridge 元件將典型的 Laravel 請求和回應轉換為 PSR-7 相容的實現:
composer require symfony/psr-http-message-bridge
composer require nyholm/psr7
安裝這些函式庫後,您可以透過在路由閉包或控制器方法上鍵入提示請求介面來取得 PSR-7 請求:
use Psr\Http\Message\ServerRequestInterface;
Route::get('/', function (ServerRequestInterface $request) {
// ...
});
[!NOTE] 如果您從路由或控制器返回一個 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');
檢索陣列輸入值
可以使用 array 方法檢索包含陣列的輸入值。此方法將始終將輸入值轉型為陣列。如果請求不包含具有給定名稱的輸入值,將返回一個空陣列:
$versions = $request->array('versions');
檢索日期輸入值
為了方便,可以使用 date 方法將包含日期/時間的輸入值作為 Carbon 實例檢索。如果請求不包含具有給定名稱的輸入值,將返回 null:
$birthday = $request->date('birthday');
date 方法接受的第二個和第三個參數分別用於指定日期的格式和時區:
$elapsed = $request->date('elapsed', '!H:i', 'Europe/Madrid');
如果輸入值存在但格式無效,將拋出 InvalidArgumentException;因此,建議在呼叫 date 方法之前驗證輸入。
檢索間隔輸入值
可以使用 interval 方法將包含持續時間的輸入值作為 CarbonInterval 實例檢索。如果請求不包含具有給定名稱的輸入值,將返回 null:
$duration = $request->interval('duration');
如果輸入值是數值的,您可以提供一個單位作為第二個參數。該單位可以是一個字串(例如 second、minute 或 day),也可以是一個 Carbon\Unit 列舉實例:
use Carbon\Unit;
$timeout = $request->interval('timeout', 'second');
$delay = $request->interval('delay', Unit::Minute);
如果輸入值存在但格式無效,將拋出 InvalidArgumentException;因此,建議在呼叫 interval 方法之前驗證輸入。
檢索列舉輸入值
對應於 PHP 列舉的輸入值也可以從請求中檢索。如果請求不包含具有給定名稱的輸入值,或者列舉沒有與輸入值匹配的支援值,將返回 null。enum 方法分別接受輸入值的名稱和列舉類別作為其第一個和第二個參數:
use App\Enums\Status;
$status = $request->enum('status', Status::class);
您還可以提供一個預設值,如果值缺失或無效則返回該值:
$status = $request->enum('status', Status::class, Status::Pending);
如果輸入值是對應於 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');
[!WARNING]
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) {
// 「name」值存在...
}, function () {
// 「name」值不存在...
});
如果您希望判斷請求中是否存在某個值且不是空字串,可以使用 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) {
// 「name」值已填寫...
}, function () {
// 「name」值未填寫...
});
要判斷請求中是否缺少給定的鍵,可以使用 missing 和 whenMissing 方法:
if ($request->missing('name')) {
// ...
}
$request->whenMissing('name', function () {
// 「name」值缺失...
}, function () {
// 「name」值存在...
});
合併額外輸入
有時您可能需要手動將額外輸入合併到請求的現有輸入資料中。為此,您可以使用 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') }}">
Cookies
從請求中檢索 Cookies
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): void {
$middleware->remove([
ConvertEmptyStringsToNull::class,
TrimStrings::class,
]);
})
如果您希望為應用程式的一部分請求禁用字串修剪和空字串轉換,可以在應用程式的 bootstrap/app.php 檔案中使用 trimStrings 和 convertEmptyStringsToNull 中介層方法。這兩個方法都接受一個閉包陣列,應返回 true 或 false 以指示是否應跳過輸入標準化:
->withMiddleware(function (Middleware $middleware): void {
$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 方法接受相對於檔案系統配置的根目錄的儲存路徑。此路徑不應包含檔名,因為將自動生成一個唯一代碼作為檔名。
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');
[!NOTE] 有關 Laravel 中檔案儲存的更多資訊,請查看完整的檔案儲存文檔。
配置可信代理
在終止 TLS / SSL 憑證的負載平衡器後面運行應用程式時,您可能會注意到您的應用程式在使用 url 輔助函數時有時不會生成 HTTPS 連結。通常這是因為您的應用程式正在從負載平衡器的連接埠 80 轉發流量,並且不知道應該生成安全連結。
要解決此問題,您可以啟用 Laravel 應用程式中包含的 Illuminate\Http\Middleware\TrustProxies 中介層,它允許您快速自訂您的應用程式應信任的負載平衡器或代理。您的可信代理應使用應用程式 bootstrap/app.php 檔案中的 trustProxies 中介層方法指定:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: [
'192.168.1.1',
'10.0.0.0/8',
]);
})
除了配置可信代理外,您還可以配置應信任的代理頁頭:
->withMiddleware(function (Middleware $middleware): void {
$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
);
})
[!NOTE] 如果您正在使用 AWS Elastic Load Balancing,
headers值應為Request::HEADER_X_FORWARDED_AWS_ELB。如果您的負載平衡器使用 RFC 7239 的標準Forwarded頁頭,headers值應為Request::HEADER_FORWARDED。有關headers值中可用常數的更多資訊,請查看 Symfony 關於信任代理的文檔。
信任所有代理
如果您正在使用 Amazon AWS 或其他「雲端」負載平衡器提供者,您可能不知道實際負載平衡器的 IP 位址。在這種情況下,您可以使用 * 來信任所有代理:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: '*');
})
配置可信主機
預設情況下,Laravel 將回應它收到的所有請求,無論 HTTP 請求的 Host 頁頭的內容如何。此外,Host 頁頭的值將在 Web 請求期間生成應用程式的絕對 URL 時使用。
通常,您應該配置您的 Web 伺服器(如 Nginx 或 Apache),使其只向您的應用程式發送匹配給定主機名的請求。但是,如果您無法直接自訂 Web 伺服器,並且需要指示 Laravel 只回應某些主機名,可以透過為應用程式啟用 Illuminate\Http\Middleware\TrustHosts 中介層來執行此操作。
要啟用 TrustHosts 中介層,您應該在應用程式的 bootstrap/app.php 檔案中呼叫 trustHosts 中介層方法。使用此方法的 at 參數,您可以指定應用程式應回應的主機名。主機名字串被視為正則表達式。具有其他 Host 頁頭的傳入請求將被拒絕:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustHosts(at: ['^laravel\.test$']);
})
預設情況下,來自應用程式 URL 子網域的請求也會被自動信任。如果您希望禁用此行為,可以使用 subdomains 參數:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustHosts(at: ['^laravel\.test$'], subdomains: false);
})
如果您需要存取應用程式的配置檔案或資料庫來判斷您的可信主機,可以向 at 參數提供一個閉包:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustHosts(at: fn () => config('app.trusted_hosts'));
})