Skip to content

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/barpath 方法將回傳 foo/bar

$uri = $request->path();

檢查請求路徑/路由

is 方法允許您驗證傳入的請求路徑是否符合給定的模式。使用此方法時,您可以使用 * 字元作為萬用字元:

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

使用 routeIs 方法,您可以判斷傳入的請求是否符合命名路由

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

擷取請求 URL

若要擷取傳入請求的完整 URL,您可以使用 urlfullUrl 方法。url 方法會回傳不含查詢字串的 URL,而 fullUrl 方法則包含查詢字串:

$url = $request->url();

$urlWithQueryString = $request->fullUrl();

如果您想將查詢字串資料附加到目前的 URL,您可以呼叫 fullUrlWithQuery 方法。此方法會將給定的查詢字串變數陣列與目前的查詢字串合併:

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

如果您想取得不含特定查詢字串參數的目前 URL,您可以使用 fullUrlWithoutQuery 方法:

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

擷取請求主機

您可以透過 hosthttpHostschemeAndHttpHost 方法擷取傳入請求的「主機」:

$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 request,您需要先安裝一些函式庫。Laravel 使用 Symfony HTTP Message Bridge 元件將典型的 Laravel 請求與回應轉換為與 PSR-7 相容的實作:

shell
composer require symfony/psr-http-message-bridge
composer require nyholm/psr7

安裝這些函式庫後,您可以在路由閉包或控制器方法中透過類型提示請求介面來取得 PSR-7 請求:

use Psr\Http\Message\ServerRequestInterface;

Route::get('/', function (ServerRequestInterface $request) {
    // ...
});

📌 備註

如果您從路由或控制器回傳一個 PSR-7 回應實例,它會自動轉換回 Laravel response 實例並由框架顯示。

輸入

擷取輸入

擷取所有輸入資料

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

$input = $request->all();

使用 collect 方法,您可以將所有傳入請求的輸入資料擷取為 collection

$input = $request->collect();

collect 方法還允許您將傳入請求的輸入子集擷取為 collection:

$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');

擷取 Stringable 輸入值

除了將請求的輸入資料擷取為原始 string 之外,您還可以使用 string 方法將請求資料擷取為 Illuminate\Support\Stringable 實例:

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

擷取整數輸入值

要將輸入值擷取為整數,您可以使用 integer 方法。此方法會嘗試將輸入值轉換為整數。如果輸入不存在或轉換失敗,它將傳回您指定的預設值。這對於分頁或其他數值輸入特別有用:

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

擷取布林值輸入

當處理 HTML 元素 (例如核取方塊) 時,您的應用程式可能會收到實際為字串的「真值」。例如,「true」或「on」。為方便起見,您可以使用 boolean 方法將這些值擷取為布林值。boolean 方法會對 1、「1」、true、「true」、「on」和「yes」傳回 true。所有其他值都將傳回 false

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

擷取日期輸入值

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

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

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

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

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

擷取 Enum 輸入值

對應於 PHP enums 的輸入值也可以從請求中擷取。如果請求不包含具有給定名稱的輸入值,或者 enum 沒有與輸入值相符的後端值,則會傳回 nullenum 方法接受輸入值的名稱和 enum 類別作為其第一個和第二個引數:

use App\Enums\Status;

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

如果輸入值是與 PHP enum 對應的值陣列,您可以使用 enums 方法將值陣列擷取為 enum 實例:

use App\Enums\Product;

$products = $request->enums('products', Product::class);

透過動態屬性擷取輸入

您也可以使用 Illuminate\Http\Request 實例上的動態屬性存取使用者輸入。例如,如果應用程式的其中一個表單包含 name 欄位,您可以這樣存取該欄位的值:

$name = $request->name;

使用動態屬性時,Laravel 會先在請求負載中尋找參數的值。如果不存在,Laravel 會在符合路由的參數中尋找該欄位。

擷取部分輸入資料

如果需要擷取輸入資料的子集,可以使用 onlyexcept 方法。這兩個方法都接受單個 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 方法就會執行給定的 Closure:

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

可以傳入第二個 Closure 到 whenHas 方法,如果請求中不存在指定的值,就會執行該 Closure:

$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 方法就會執行給定的 Closure:

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

可以傳入第二個 Closure 到 whenFilled 方法,如果指定的值不是「filled」,就會執行該 Closure:

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

要判斷請求中是否缺少某個鍵,可以使用 missingwhenMissing 方法:

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 內建的驗證功能,您可能不需要直接手動使用這些 Session 輸入快閃方法,因為 Laravel 內建的一些驗證功能會自動呼叫它們。

將輸入快閃到 Session

Illuminate\Http\Request 類別上的 flash 方法會將目前輸入快閃到Session 中,以便在使用者對應用程式的下一個請求期間可以使用:

$request->flash();

您也可以使用 flashOnlyflashExcept 方法將請求資料的子集快閃到 Session 中。這些方法對於將密碼等敏感資訊排除在 Session 之外很有用:

$request->flashOnly(['username', 'email']);

$request->flashExcept('password');

快閃輸入然後重定向

由於您通常會希望將輸入快閃到 Session,然後重定向到上一頁,因此您可以使用 withInput 方法輕鬆地將輸入快閃功能串接到重定向上:

return redirect('/form')->withInput();

return redirect()->route('user.create')->withInput();

return redirect('/form')->withInput(
    $request->except('password')
);

擷取舊有輸入

要從前一個請求中擷取快閃的輸入,請在 Illuminate\Http\Request 實例上呼叫 old 方法。old 方法會從Session 中取出先前快閃的輸入資料:

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

Laravel 也提供了一個全域的 old 輔助函式。如果您在 Blade 模板中顯示舊有輸入,使用 old 輔助函式來重新填入表單會更方便。如果指定欄位沒有舊有輸入,將會回傳 null

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

所有由 Laravel 框架建立的 Cookie 都會使用驗證碼加密和簽署,這表示如果它們被客戶端更改過,將會被視為無效。要從請求中擷取 Cookie 值,請在 Illuminate\Http\Request 實例上使用 cookie 方法:

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

輸入修剪與正規化

預設情況下,Laravel 在您的應用程式全域中介層堆疊中包含了 Illuminate\Foundation\Http\Middleware\TrimStringsIlluminate\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 檔案中使用 trimStringsconvertEmptyStringsToNull 中介層方法。這兩個方法都接受一個閉包陣列,這些閉包應該返回 truefalse,以指示是否應跳過輸入正規化:

->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 文件以獲取有關這些方法的更多資訊。

儲存上傳檔案

要儲存上傳檔案,您通常會使用您已配置的其中一個 filesystemsUploadedFile 類別有一個 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 標頭內容為何。此外,Host 標頭的值將用於在網頁請求期間為您的應用程式產生絕對 URL。

通常,您應該配置您的網頁伺服器(例如 Nginx 或 Apache),使其只將符合指定主機名稱的請求傳送給您的應用程式。然而,如果您無法直接自訂網頁伺服器,並且需要指示 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'));
})