跳轉至內容

HTTP 請求

簡介

Laravel 的 Illuminate\Http\Request 類提供了一種面向物件的方式來與應用程式當前處理的 HTTP 請求進行互動,並獲取隨請求提交的輸入、Cookie 和檔案。

與請求互動

訪問請求

要透過依賴注入獲取當前 HTTP 請求的例項,你應該在路由閉包或控制器方法中對 Illuminate\Http\Request 類進行型別提示。傳入的請求例項將由 Laravel 服務容器 自動注入。

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Http\RedirectResponse;
6use Illuminate\Http\Request;
7 
8class UserController extends Controller
9{
10 /**
11 * Store a new user.
12 */
13 public function store(Request $request): RedirectResponse
14 {
15 $name = $request->input('name');
16 
17 // Store the user...
18 
19 return redirect('/users');
20 }
21}

如上所述,你也可以在路由閉包中對 Illuminate\Http\Request 類進行型別提示。當閉包執行時,服務容器會自動將傳入的請求注入到閉包中。

1use Illuminate\Http\Request;
2 
3Route::get('/', function (Request $request) {
4 // ...
5});

依賴注入與路由引數

如果你的控制器方法還需要從路由引數中獲取輸入,你應該將路由引數放在其他依賴項之後。例如,如果你的路由定義如下

1use App\Http\Controllers\UserController;
2 
3Route::put('/user/{id}', [UserController::class, 'update']);

你仍然可以對 Illuminate\Http\Request 進行型別提示,並透過以下方式定義控制器方法來訪問 id 路由引數

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Http\RedirectResponse;
6use Illuminate\Http\Request;
7 
8class UserController extends Controller
9{
10 /**
11 * Update the specified user.
12 */
13 public function update(Request $request, string $id): RedirectResponse
14 {
15 // Update the user...
16 
17 return redirect('/users');
18 }
19}

請求路徑、主機和方法

Illuminate\Http\Request 例項提供了多種方法來檢查傳入的 HTTP 請求,並擴充套件了 Symfony\Component\HttpFoundation\Request 類。下面我們將討論其中一些最重要的方法。

獲取請求路徑

path 方法返回請求的路徑資訊。因此,如果傳入的請求目標是 http://example.com/foo/barpath 方法將返回 foo/bar

1$uri = $request->path();

檢查請求路徑 / 路由

is 方法允許你驗證傳入的請求路徑是否匹配指定的模式。在使用此方法時,你可以使用 * 字元作為萬用字元。

1if ($request->is('admin/*')) {
2 // ...
3}

使用 routeIs 方法,你可以確定傳入的請求是否匹配某個 命名路由

1if ($request->routeIs('admin.*')) {
2 // ...
3}

獲取請求 URL

要獲取傳入請求的完整 URL,你可以使用 urlfullUrl 方法。url 方法將返回不帶查詢字串的 URL,而 fullUrl 方法則包含查詢字串。

1$url = $request->url();
2 
3$urlWithQueryString = $request->fullUrl();

如果你想向當前 URL 追加查詢字串資料,可以呼叫 fullUrlWithQuery 方法。此方法會將給定的查詢字串變數陣列與當前查詢字串合併。

1$request->fullUrlWithQuery(['type' => 'phone']);

如果你想在獲取當前 URL 時排除某個給定的查詢字串引數,可以使用 fullUrlWithoutQuery 方法。

1$request->fullUrlWithoutQuery(['type']);

獲取請求主機

你可以透過 hosthttpHostschemeAndHttpHost 方法獲取傳入請求的“主機”。

1$request->host();
2$request->httpHost();
3$request->schemeAndHttpHost();

獲取請求方法

method 方法將返回請求的 HTTP 動詞。你可以使用 isMethod 方法來驗證 HTTP 動詞是否與給定的字串匹配。

1$method = $request->method();
2 
3if ($request->isMethod('post')) {
4 // ...
5}

請求頭

你可以使用 header 方法從 Illuminate\Http\Request 例項中獲取請求頭。如果請求中不存在該頭資訊,則返回 null。不過,header 方法接受一個可選的第二個引數,如果頭資訊不存在,將返回該引數的值。

1$value = $request->header('X-Header-Name');
2 
3$value = $request->header('X-Header-Name', 'default');

hasHeader 方法可用於確定請求是否包含給定的頭資訊。

1if ($request->hasHeader('X-Header-Name')) {
2 // ...
3}

為方便起見,可以使用 bearerToken 方法從 Authorization 頭中獲取令牌。如果不存在此類頭資訊,則返回空字串。

1$token = $request->bearerToken();

請求 IP 地址

ip 方法可用於獲取向你的應用程式發起請求的客戶端 IP 地址。

1$ipAddress = $request->ip();

如果你想獲取 IP 地址陣列(包括代理轉發的所有客戶端 IP 地址),可以使用 ips 方法。“原始”客戶端 IP 地址將位於陣列的末尾。

1$ipAddresses = $request->ips();

通常情況下,IP 地址應被視為不可信的、受使用者控制的輸入,僅供參考。

內容協商

Laravel 提供了幾種透過 Accept 頭檢查傳入請求所需內容型別的方法。首先,getAcceptableContentTypes 方法將返回一個包含請求所接受的所有內容型別的陣列。

1$contentTypes = $request->getAcceptableContentTypes();

accepts 方法接受一個內容型別陣列,如果請求接受其中任何一種內容型別,則返回 true,否則返回 false

1if ($request->accepts(['text/html', 'application/json'])) {
2 // ...
3}

你可以使用 prefers 方法來確定給定內容型別陣列中,請求最偏好哪一種。如果請求不接受所提供的任何內容型別,則返回 null

1$preferred = $request->prefers(['text/html', 'application/json']);

由於許多應用程式只提供 HTML 或 JSON,你可以使用 expectsJson 方法快速確定傳入的請求是否期望 JSON 響應。

1if ($request->expectsJson()) {
2 // ...
3}

如果你需要確定請求是否特別偏好 Markdown,或者在其他內容型別中是否接受 Markdown(例如在服務於 AI 智慧體或其他消費 Markdown 響應的客戶端時),可以使用 wantsMarkdownacceptsMarkdown 方法。

1if ($request->wantsMarkdown()) {
2 // The client's most preferred content type is text/markdown...
3}
4 
5if ($request->acceptsMarkdown()) {
6 // The client accepts Markdown responses...
7}

PSR-7 請求

PSR-7 標準 規定了 HTTP 訊息(包括請求和響應)的介面。如果你想獲取 PSR-7 請求例項而不是 Laravel 請求例項,首先需要安裝幾個庫。Laravel 使用 Symfony HTTP Message Bridge 元件將典型的 Laravel 請求和響應轉換為相容 PSR-7 的實現。

1composer require symfony/psr-http-message-bridge
2composer require nyholm/psr7

安裝這些庫後,你可以在路由閉包或控制器方法中對請求介面進行型別提示,從而獲取 PSR-7 請求。

1use Psr\Http\Message\ServerRequestInterface;
2 
3Route::get('/', function (ServerRequestInterface $request) {
4 // ...
5});

如果你從路由或控制器返回一個 PSR-7 響應例項,它將自動轉換回 Laravel 響應例項並由框架顯示。

輸入

獲取輸入

獲取所有輸入資料

你可以使用 all 方法將所有傳入請求的輸入資料作為 array 獲取。無論傳入請求是來自 HTML 表單還是 XHR 請求,此方法均可使用。

1$input = $request->all();

使用 collect 方法,你可以將所有傳入請求的輸入資料作為 集合 (collection) 獲取。

1$input = $request->collect();

collect 方法還允許你獲取傳入請求輸入的子集作為集合。

1$request->collect('users')->each(function (string $user) {
2 // ...
3});

獲取輸入值

使用一些簡單的方法,你可以從 Illuminate\Http\Request 例項中訪問所有使用者輸入,而無需擔心請求使用的是哪種 HTTP 動詞。無論使用何種 HTTP 動詞,都可以使用 input 方法來獲取使用者輸入。

1$name = $request->input('name');

你可以將預設值作為第二個引數傳遞給 input 方法。如果請求中不存在所請求的輸入值,則會返回該值。

1$name = $request->input('name', 'Sally');

處理包含陣列輸入的表單時,請使用“點”符號來訪問陣列。

1$name = $request->input('products.0.name');
2 
3$names = $request->input('products.*.name');

你可以不帶任何引數呼叫 input 方法,以關聯陣列的形式獲取所有輸入值。

1$input = $request->input();

從查詢字串中獲取輸入

雖然 input 方法從整個請求負載(包括查詢字串)中獲取值,但 query 方法僅從查詢字串中獲取值。

1$name = $request->query('name');

如果所請求的查詢字串資料不存在,將返回此方法的第二個引數。

1$name = $request->query('name', 'Helen');

你可以不帶任何引數呼叫 query 方法,以關聯陣列的形式獲取所有查詢字串值。

1$query = $request->query();

獲取 JSON 輸入值

當嚮應用程式傳送 JSON 請求時,只要請求的 Content-Type 頭已正確設定為 application/json,你就可以透過 input 方法訪問 JSON 資料。你甚至可以使用“點”語法來獲取 JSON 陣列/物件中巢狀的值。

1$name = $request->input('user.name');

獲取可字串化的輸入值

與其將請求輸入資料作為原始 string 獲取,不如使用 string 方法將請求資料作為 Illuminate\Support\Stringable 例項獲取。

1$name = $request->string('name')->trim();

獲取整數輸入值

要將輸入值作為整數獲取,可以使用 integer 方法。此方法將嘗試把輸入值強制轉換為整數。如果輸入不存在或轉換失敗,它將返回你指定的預設值。這對於分頁或其他數字輸入特別有用。

1$perPage = $request->integer('per_page');

獲取布林輸入值

在處理複選框等 HTML 元素時,應用程式可能會收到實際上是字串的“真值”。例如 "true" 或 "on"。為方便起見,你可以使用 boolean 方法將這些值作為布林值獲取。boolean 方法對於 1, "1", true, "true", "on" 和 "yes" 返回 true。所有其他值都將返回 false

1$archived = $request->boolean('archived');

獲取陣列輸入值

可以使用 array 方法獲取包含陣列的輸入值。此方法始終會將輸入值強制轉換為陣列。如果請求不包含具有給定名稱的輸入值,則返回一個空陣列。

1$versions = $request->array('versions');

獲取日期輸入值

為方便起見,可以使用 date 方法將包含日期/時間的輸入值作為 Carbon 例項獲取。如果請求不包含具有給定名稱的輸入值,則返回 null

1$birthday = $request->date('birthday');

date 方法接受的第二個和第三個引數分別用於指定日期的格式和時區。

1$elapsed = $request->date('elapsed', '!H:i', 'Europe/Madrid');

如果輸入值存在但格式無效,則會丟擲 InvalidArgumentException;因此,建議在呼叫 date 方法之前先驗證輸入。

獲取列舉輸入值

對應於 PHP 列舉 的輸入值也可以從請求中獲取。如果請求不包含具有給定名稱的輸入值,或者列舉沒有與輸入值匹配的支援值,則返回 nullenum 方法接受輸入值的名稱和列舉類作為其第一個和第二個引數。

1use App\Enums\Status;
2 
3$status = $request->enum('status', Status::class);

你還可以提供一個預設值,如果值缺失或無效,則返回該預設值。

1$status = $request->enum('status', Status::class, Status::Pending);

如果輸入值是一個對應於 PHP 列舉的值陣列,你可以使用 enums 方法將這些值作為列舉例項陣列獲取。

1use App\Enums\Product;
2 
3$products = $request->enums('products', Product::class);

透過動態屬性獲取輸入

你也可以使用 Illuminate\Http\Request 例項上的動態屬性來訪問使用者輸入。例如,如果應用程式的表單包含一個 name 欄位,你可以這樣訪問該欄位的值。

1$name = $request->name;

使用動態屬性時,Laravel 會首先在請求負載中查詢引數值。如果不存在,Laravel 將在匹配的路由引數中搜索該欄位。

獲取輸入資料的一部分

如果你需要獲取輸入資料的子集,可以使用 onlyexcept 方法。這兩個方法都接受單個 array 或動態引數列表。

1$input = $request->only(['username', 'password']);
2 
3$input = $request->only('username', 'password');
4 
5$input = $request->except(['credit_card']);
6 
7$input = $request->except('credit_card');

only 方法返回你請求的所有鍵/值對;但是,它不會返回請求中不存在的鍵/值對。

檢查輸入是否存在

你可以使用 has 方法來確定值是否存在於請求中。如果值存在於請求中,has 方法返回 true

1if ($request->has('name')) {
2 // ...
3}

當給定陣列時,has 方法將確定是否所有指定的值都存在。

1if ($request->has(['name', 'email'])) {
2 // ...
3}

如果存在任何指定的值,hasAny 方法返回 true

1if ($request->hasAny(['name', 'email'])) {
2 // ...
3}

如果值存在於請求中,whenHas 方法將執行給定的閉包。

1$request->whenHas('name', function (string $input) {
2 // ...
3});

可以向 whenHas 方法傳遞第二個閉包,如果請求中不存在指定的值,則會執行該閉包。

1$request->whenHas('name', function (string $input) {
2 // The "name" value is present...
3}, function () {
4 // The "name" value is not present...
5});

如果你想確定值是否存在於請求中且不是空字串,可以使用 filled 方法。

1if ($request->filled('name')) {
2 // ...
3}

如果你想確定值是否從請求中缺失或者是空字串,可以使用 isNotFilled 方法。

1if ($request->isNotFilled('name')) {
2 // ...
3}

當給定陣列時,isNotFilled 方法將確定是否所有指定的值都缺失或為空。

1if ($request->isNotFilled(['name', 'email'])) {
2 // ...
3}

如果任何指定的值不是空字串,anyFilled 方法返回 true

1if ($request->anyFilled(['name', 'email'])) {
2 // ...
3}

如果值存在於請求中且不是空字串,whenFilled 方法將執行給定的閉包。

1$request->whenFilled('name', function (string $input) {
2 // ...
3});

可以向 whenFilled 方法傳遞第二個閉包,如果指定的值沒有“填滿”,則會執行該閉包。

1$request->whenFilled('name', function (string $input) {
2 // The "name" value is filled...
3}, function () {
4 // The "name" value is not filled...
5});

要確定給定的鍵是否從請求中缺失,可以使用 missingwhenMissing 方法。

1if ($request->missing('name')) {
2 // ...
3}
4 
5$request->whenMissing('name', function () {
6 // The "name" value is missing...
7}, function () {
8 // The "name" value is present...
9});

合併額外輸入

有時你可能需要手動將額外的輸入合併到請求現有的輸入資料中。為此,你可以使用 merge 方法。如果給定的輸入鍵已經存在於請求中,它將被 merge 方法提供的資料覆蓋。

1$request->merge(['votes' => 0]);

如果相應的鍵尚未存在於請求的輸入資料中,可以使用 mergeIfMissing 方法將輸入合併到請求中。

1$request->mergeIfMissing(['votes' => 0]);

舊輸入

Laravel 允許你在下一次請求期間保留本次請求的輸入。此功能對於在檢測到驗證錯誤後重新填充表單特別有用。但是,如果你正在使用 Laravel 附帶的 驗證功能,你可能不需要直接手動使用這些會話輸入快閃記憶體方法,因為 Laravel 的一些內建驗證工具會自動呼叫它們。

將輸入快閃記憶體到會話

Illuminate\Http\Request 類上的 flash 方法會將當前輸入快閃記憶體到 會話 (session) 中,以便在使用者對應用程式的下一次請求期間可以使用這些輸入。

1$request->flash();

你還可以使用 flashOnlyflashExcept 方法將請求資料的子集快閃記憶體到會話中。這些方法對於將密碼等敏感資訊排除在會話之外非常有用。

1$request->flashOnly(['username', 'email']);
2 
3$request->flashExcept('password');

快閃記憶體輸入並重定向

由於你通常希望將輸入快閃記憶體到會話中,然後重定向到上一頁,你可以使用 withInput 方法輕鬆地將輸入快閃記憶體連結到重定向中。

1return redirect('/form')->withInput();
2 
3return redirect()->route('user.create')->withInput();
4 
5return redirect('/form')->withInput(
6 $request->except('password')
7);

獲取舊輸入

要獲取上一次請求的快閃記憶體輸入,請在 Illuminate\Http\Request 例項上呼叫 old 方法。old 方法將從 會話 (session) 中提取先前快閃記憶體的輸入資料。

1$username = $request->old('username');

Laravel 還提供了一個全域性 old 輔助函式。如果你要在 Blade 模板 中顯示舊輸入,使用 old 輔助函式重新填充表單會更方便。如果給定欄位沒有舊輸入,則返回 null

1<input type="text" name="username" value="{{ old('username') }}">

Cookies

從請求中獲取 Cookie

Laravel 框架建立的所有 Cookie 都經過加密並使用身份驗證程式碼簽名,這意味著如果它們被客戶端更改,將被視為無效。要從請求中獲取 Cookie 值,請在 Illuminate\Http\Request 例項上使用 cookie 方法。

1$value = $request->cookie('name');

輸入修剪與規範化

預設情況下,Laravel 在應用程式的全域性中介軟體棧中包含了 Illuminate\Foundation\Http\Middleware\TrimStringsIlluminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull 中介軟體。這些中介軟體會自動修剪請求中的所有傳入字串欄位,並將所有空字串欄位轉換為 null。這使你無需在路由和控制器中擔心這些規範化問題。

停用輸入規範化

如果你想為所有請求停用此行為,可以透過在應用程式的 bootstrap/app.php 檔案中呼叫 $middleware->remove 方法,從應用程式的中介軟體棧中移除這兩個中介軟體。

1use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
2use Illuminate\Foundation\Http\Middleware\TrimStrings;
3 
4->withMiddleware(function (Middleware $middleware): void {
5 $middleware->remove([
6 ConvertEmptyStringsToNull::class,
7 TrimStrings::class,
8 ]);
9})

如果你想為應用程式的部分請求停用字串修剪和空字串轉換,可以在應用程式的 bootstrap/app.php 檔案中使用 trimStringsconvertEmptyStringsToNull 中介軟體方法。這兩個方法都接受一個閉包陣列,這些閉包應返回 truefalse,以指示是否應跳過輸入規範化。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->convertEmptyStringsToNull(except: [
3 fn (Request $request) => $request->is('admin/*'),
4 ]);
5 
6 $middleware->trimStrings(except: [
7 fn (Request $request) => $request->is('admin/*'),
8 ]);
9})

檔案

獲取上傳檔案

你可以使用 file 方法或動態屬性從 Illuminate\Http\Request 例項中獲取上傳的檔案。file 方法返回 Illuminate\Http\UploadedFile 類的例項,該類擴充套件了 PHP 的 SplFileInfo 類,並提供了多種與檔案互動的方法。

1$file = $request->file('photo');
2 
3$file = $request->photo;

你可以使用 hasFile 方法確定請求中是否存在檔案。

1if ($request->hasFile('photo')) {
2 // ...
3}

驗證成功上傳

除了檢查檔案是否存在外,你還可以透過 isValid 方法驗證上傳檔案時是否有問題。

1if ($request->file('photo')->isValid()) {
2 // ...
3}

檔案路徑和副檔名

UploadedFile 類還包含用於訪問檔案完全限定路徑及其副檔名的方法。extension 方法將嘗試根據其內容猜測檔案的副檔名。此副檔名可能與客戶端提供的副檔名不同。

1$path = $request->photo->path();
2 
3$extension = $request->photo->extension();

其他檔案方法

UploadedFile 例項上還有許多其他可用方法。檢視 該類的 API 文件 以獲取有關這些方法的更多資訊。

儲存上傳檔案

要儲存上傳的檔案,你通常會使用配置好的 檔案系統 (filesystem) 之一。UploadedFile 類有一個 store 方法,可以將上傳的檔案移動到你的磁碟之一,該磁碟可以是本地檔案系統上的位置,也可以是像 Amazon S3 這樣的雲端儲存位置。

store 方法接受相對於檔案系統配置的根目錄儲存檔案的路徑。此路徑不應包含檔名,因為會自動生成一個唯一 ID 作為檔名。

store 方法還接受一個可選的第二個引數,用於指定應儲存檔案的磁碟名稱。該方法將返回相對於磁碟根目錄的檔案路徑。

1$path = $request->photo->store('images');
2 
3$path = $request->photo->store('images', 's3');

如果你不希望自動生成檔名,可以使用 storeAs 方法,它接受路徑、檔名和磁碟名稱作為引數。

1$path = $request->photo->storeAs('images', 'filename.jpg');
2 
3$path = $request->photo->storeAs('images', 'filename.jpg', 's3');

有關 Laravel 中檔案儲存的更多資訊,請檢視完整的 檔案儲存文件

配置受信任的代理

當在終止 TLS / SSL 證書的負載均衡器後執行應用程式時,你可能會注意到使用 url 輔助函式時,應用程式有時不會生成 HTTPS 連結。這通常是因為你的應用程式正透過埠 80 從負載均衡器轉發流量,而它不知道應該生成安全連結。

要解決這個問題,你可以啟用 Laravel 應用程式中包含的 Illuminate\Http\Middleware\TrustProxies 中介軟體,它允許你快速自定義應用程式應信任的負載均衡器或代理。你的受信任代理應使用應用程式 bootstrap/app.php 檔案中的 trustProxies 中介軟體方法來指定。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustProxies(at: [
3 '192.168.1.1',
4 '10.0.0.0/8',
5 ]);
6})

除了配置受信任的代理外,你還可以配置應信任的代理頭。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR |
3 Request::HEADER_X_FORWARDED_HOST |
4 Request::HEADER_X_FORWARDED_PORT |
5 Request::HEADER_X_FORWARDED_PROTO |
6 Request::HEADER_X_FORWARDED_AWS_ELB
7 );
8})

如果你使用的是 AWS Elastic Load Balancing,headers 值應為 Request::HEADER_X_FORWARDED_AWS_ELB。如果你的負載均衡器使用來自 RFC 7239 的標準 Forwarded 頭,則 headers 值應為 Request::HEADER_FORWARDED。有關 headers 值中可使用的常量的更多資訊,請參閱 Symfony 關於 信任代理 的文件。

信任所有代理

如果你使用的是 Amazon AWS 或其他“雲”負載均衡器提供商,你可能不知道實際負載均衡器的 IP 地址。在這種情況下,你可以使用 * 來信任所有代理。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustProxies(at: '*');
3})

配置受信任的主機

預設情況下,無論 HTTP 請求的 Host 頭內容如何,Laravel 都會響應它接收到的所有請求。此外,在 Web 請求期間為應用程式生成絕對 URL 時,將使用 Host 頭的值。

通常,你應該配置 Web 伺服器(如 Nginx 或 Apache)僅向匹配給定主機名的應用程式傳送請求。但是,如果你無法直接自定義 Web 伺服器,並且需要指示 Laravel 僅響應某些主機名,則可以透過為應用程式啟用 Illuminate\Http\Middleware\TrustHosts 中介軟體來實現。

要啟用 TrustHosts 中介軟體,你應該在應用程式的 bootstrap/app.php 檔案中呼叫 trustHosts 中介軟體方法。使用此方法的 at 引數,你可以指定應用程式應響應的主機名。主機名字串被視為正則表示式。具有其他 Host 頭的傳入請求將被拒絕。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustHosts(at: ['^laravel\.test$']);
3})

預設情況下,來自應用程式 URL 子域的請求也會自動被信任。如果你想停用此行為,可以使用 subdomains 引數。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustHosts(at: ['^laravel\.test$'], subdomains: false);
3})

如果你需要訪問應用程式的配置檔案或資料庫來確定受信任的主機,你可以向 at 引數提供一個閉包。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustHosts(at: fn () => config('app.trusted_hosts'));
3})