HTTP Request

簡介

Laravel 的 Illuminate\Http\Request 類別提供了一種物件導向的方法來讓你存取目前程式在處理的 HTTP Request,包含 Request 的輸入、Cookie、上傳的檔案⋯⋯等。

使用 Request

存取 Request

若要通過相依性插入 (Dependency Injection) 來取得目前的 HTTP Request,可在 Route 閉包或 Controller 方法上型別提示 Illuminate\Http\Request 類別。連入的 Request 實體會自動被插入到 Laravel 的 Service Container

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

剛才也提到過,我們也可以在 Route 閉包上型別提示 Illuminate\Http\Request 類別。Service Container 會自動在閉包執行時將連入的 Request 插入進去:

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

相依性插入與 Route 參數

若 Controller 方法中還會從 Route 引數中收到輸入,請將 Route 引數列在其他相依性之後。舉例來說,若 Route 定義長這樣:

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

則我們還是可以像這樣定義 Controller 方法來型別提示 Illuminate\Http\Request 並取得 id Route 參數:

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

Request 路徑、主機、與方法

Illuminate\Http\Request 提供了多種可檢查連入 HTTP Request 的方法。這個方法也繼承了 Symfony\Component\HttpFoundation\Request 類別。我們稍後會討論其中幾個最重要的方法。

取得 Request 的路徑

path 方法會回傳 Request 的路徑資訊。因此,若連入 Request 是在瀏覽 http://example.com/foo/bar,則 path 方法會回傳 foo/bar

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

偵測 Request 路徑與 Route

可以使用 is 方法來驗證連入 Request 的路徑是否符合給定的格式。使用這個方法的時候,可以使用 * 字元作為萬用字元:

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

使用 routeIs 方法可以判斷連入的 Request 是否為某個命名 Route

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

取得 Request 的 URL

若要取得連入 Request 的完整 URL,可以使用 urlfullUrl 方法。url 方法會回傳不含查詢字串 (Query String) 的 URL,而 fullUrl 則包含查詢字串:

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

若想將查詢字串資料附加到目前的 URL,可以使用 fullUrlWithQuery 方法。傳入一個包含查詢字串變數的陣列,然後這個方法會將給定的陣列與目前的查詢字串合併:

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

取得 Request 主機

可以使用 hosthttpHost、與 schemeAndHttpHost 來取得連入 Request 的「主機」:

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

取得 Request 的方法

method 方法會回傳該 Request 的 HTTP 動詞 (Verb)。可以使用 isMethod 方法來判斷目前的 HTTP 動詞是否符合給定字串:

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

Request 標頭

可以使用 header 方法來從 Illuminate\Http\Request 內取得 Request 的標頭 (Header)。若該 Request 未包含指定的標頭,則會回傳 null。不過,header 方法也接受第三個可選的引數,會在標頭不存在時回傳該值:

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

hasHeader 方法可用來判斷 Request 是否包含給定的標頭:

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

為了方便起見,可以使用 bearerToken 方法來從 Authorization 標頭中取得 Bearer Token。若該標頭不存在,會回傳空字串:

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

Request 的 IP 位址

可以使用 ip 方法來取得用戶端發起 Request 使用的 IP 位址:

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

判斷適當的內容

Laravel 提供了數種方法來通過 Accept 標頭判斷連入 Request 所要求的 Content Type (內容類型)。首先,getAcceptableContentTypes 會回傳一個陣列,其中包含該 Request 所接受的所有 Content Type:

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

accepts 方法接受一個包含 Content Type 的陣列,當陣列中有任何一個 Content Type 是 Request 接受的,就會回傳 true。否則,會回傳 false

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

可以使用 prefers 方法來判斷給定陣列中的哪個 Content Type 是該 Request 最優先選擇的。若所提供的 Content Type 都不為 Request 接受,則會回傳 null

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

因為大部分專案都只提供 HTML 或 JSON,所以我們可以通過 expectsJson 方法來快速判斷連入的 Request 是否預期 Response 應為 JSON:

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

PSR-7 Request

PSR-7 標準 指定了用於 HTTP 訊息通訊的介面,其中包含 Request 與 Response。若你想取得 PSR-7 Request 的實體而不是 Laravel Request,首先你會需要安裝幾個函式庫。Laravel 使用 Symfony 的 HTTP Message Bridge 元件來將一般的 Laravel Request 與 Response 轉換為相容於 PSR-7 的實作:

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

安裝好這些函式庫後,就可以在 Route 閉包或 Controller 方法上型別提示 PSR-7 Request 介面來取得 PSR-7 Request 的實體:

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

若從 Route 或 Controller 中回傳 PSR-7 Response,這個 Response 會先被轉回到 Laravel 的 Response 實體,然後才會由 Laravel 顯示出來。

輸入

取得輸入

取得所有輸入的資料

可以使用 all 方法來將所有連入 Request 的輸入資料取得為 array。無論連入的 Request 是來自 HTML 表單還是 XHR Request,都可以使用這個方法:

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

使用 collect 方法就可以將連入 Request 的輸入資料作為 Collection 取得:

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

使用 collect 方法也可以用來將連入 Request 輸入中的一部分取得為 Collection:

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

取得輸入值

使用幾個簡單的方法,不需要擔心 Request 使用了哪個 HTTP 動詞,都可以存取 Illuminate\Http\Request 實體中所有的使用者輸入。無論 HTTP 動詞是什麼,都可以用 input 方法來取得使用者輸入:

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

也可以傳入第二個引數給 input 方法來取得預設值。若 Request 中沒有要求的輸入值時,就會回傳這個預設值:

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

在處理包含陣列輸入的表單時,可以使用「點 (.)」標記法來存取陣列:

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

呼叫 input 方法時若不傳入任何引數,則可以用關聯式陣列的方式取得所有輸入資料:

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

取得查詢字串上的輸入

雖然 input 方法可以從所有的 Request 承載 (Payload) 上取得資料 (其中也包含查詢字串),若使用 query 方法,則可以只從查詢字串中取得資料:

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

若要求的查詢字串值不存在,則會回傳第二個傳入該方法的值:

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

呼叫 query 方法時若不傳入任何引數,則可以用關聯式陣列的方式取得所有查詢字串的資料:

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

取得 JSON 輸入值

傳送 JSON 的 Request 時,只要 Request 的 Content-Type 由正確設定為 application/json,就可以使用 input 方法來存取 JSON 資料。也可以使用「點 (.)」標記法來存取 JSON 陣列/物件中的巢狀資料:

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

取得 Stringable 的輸入值

除了將輸入值以原生型別的 string 取得,還可以使用 string 方法來將 Request 資料以 Illuminate\Support\Stringable 實體的形式取得:

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

取得布林輸入值

在處理如勾選框 (Checkbox) 等 HTML 元素時,我們的程式可能會收到以字串形式呈現的「真假」值。舉例來說,這個值可能是「true」或「on」。為了方便起見,我們可以使用 boolean 方法來將這些值以布林方式取得。值為 1、"1"、true、"true"、"on"、"yes" 時,boolean 方法回傳 true。其他任何的值則會回傳 false

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

取得日期的輸入值

為了方便起見,我們可以使用 date 方法來將包含日期 / 時間的輸入值以 Carbon 實體來存取。若 Request 中為包含給定名稱的輸入值,則會回傳 null

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

可以使用 date 的第二與第三個引數來分別指定日期的格式與時區:

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

若輸入中有值,但格式不正確時,會擲回 InvalidArgumentException。因此,建議你在叫用 date 方法前先驗證輸入。

取得 Enum 輸入值

也可以從 Request 中取得對應到 PHP Enum 的輸入值。若 Request 中沒有輸入值,或是給定的名稱或 Enum 中沒有符合該輸入值的後端值 (Backing Value),則會回傳 nullenum 方法的第一個引數為輸入值的名稱、第二個引數為 Enum 類別:

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

通過動態屬性來取得輸入

可以在 Illuminate\Http\Request 實體上通過動態屬性來存取使用者輸入。舉例來說,若其中一個程式的表單包含了 name 欄位,則可以像這樣存取該欄位的值:

1$name = $request->name;
1$name = $request->name;

使用動態方法時,Laravel 會先在 Request 的 Payload (承載) 上尋找參數值。若 Payload 上沒有該值,Laravel 會接著在 Route 參數中尋找符合名稱的欄位:

取得部分輸入資料

若只想取得一部分的輸入資料,可以使用 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');
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');
exclamation

only 方法會回傳所要求的所有索引鍵 / 值配對組。不過,若要求的索引鍵 / 值配對未出現在 Request 中,將不會回傳。

判斷輸入是否存在

可以使用 has 方法來判斷某個值是否存在 Request 中。若給定的輸入值存在於 Request 中,has 方法會回傳 true

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

傳入陣列時,has 方法判斷其中所有的值是否都存在:

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

whenHas 方法會執行給定的閉包來判斷某個值是否存在於 Request 中:

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

可以傳入第二個閉包給 whenHas 方法,當指定的值未存在於 Request 中,則會執行這個閉包:

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

hasAny 方法會給定的值有其中一個存在時回傳 true

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

若想判斷某個值是否有出現在 Request 中,且該值不是空字串時,可使用 filled 方法:

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

whenFilled 方法會執行給定的閉包來判斷 Request 中某個值是否為空字串:

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

可以傳入第二個閉包給 whenFilled 方法,當 Request 中指定的值為空時會執行這個閉包:

1$request->whenFilled('name', function ($input) {
2 // 已填寫「name」...
3}, function () {
4 // 未填寫「name」...
5});
1$request->whenFilled('name', function ($input) {
2 // 已填寫「name」...
3}, function () {
4 // 未填寫「name」...
5});

若要判斷 Request 中是否不存在給定的索引鍵,可使用 missing 方法與 whenMissing 方法:

1if ($request->missing('name')) {
2 //
3}
4 
5$request->whenMissing('name', function ($input) {
6 // 沒有「name」值...
7}, function () {
8 // 有「name」值...
9});
1if ($request->missing('name')) {
2 //
3}
4 
5$request->whenMissing('name', function ($input) {
6 // 沒有「name」值...
7}, function () {
8 // 有「name」值...
9});

合併額外的輸入

有時候,我們需要手動合併額外的輸入到 Request 中現有的輸入資料。這種情況下,可使用 merge 方法。若 Request 中已存在給定的輸入索引鍵,則會使用提供給 merge 方法的資料來複寫:

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

使用 mergeIfMissing 方法就可以只在 Request 的輸入資料中缺少特定索引鍵時才合併進 Request:

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

舊輸入

Laravel 提供了將輸入資料從一個 Request 帶到下一個 Request 的功能。這個功能特別適合用在表單驗證失敗後要重新填充表單時。不過,若你使用 Laravel 提供的表單驗證功能,那麼你應該不需要直接手動進行這些 Session 的 Input 快閃方法,因為 Laravel 的內建表單驗證功能已經自動處理好了。

將輸入資料快閃進 Session

使用 Illuminate\Http\Request 類別的 flash 方法,就可以將目前的輸入快閃 (Flash) 進 Session。這樣一來,使用者的下個 Request 中就有這些輸入值可用:

1$request->flash();
1$request->flash();

也可以使用 flashOnlyflashExcept 方法來只將一部分的 Request 資料刷入 Session。這些方法特別適用於想讓一些機密資料(如密碼)不要被刷入 Session 時:

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

快閃存入輸入後再重新導向

由於我們很常會需要再將輸入資料快閃存入 Session 後再重新導向回上一頁,因此我們只要把 withInput 方法串到重新導向後,就可以輕鬆地快閃存入輸入值:

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

取得舊輸入

若要取得前一個 Request 中的快閃輸入,可叫用 Illuminate\Http\Request 上的 old 方法。old 方法從 Session 中拉取前次快閃存入輸入資料:

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

Laravel 也提供了一個全域 old 輔助函式。若想在 Blade 樣板中顯示舊輸入,那麼使用 old 輔助函式來將其填回表單回比較方便。若給定欄位沒有舊輸入的話,會回傳 null

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

Cookie

從 Request 中取得 Cookie

所有由 Laravel 框架所建立的 Cookie 都是經過加密且使用驗證碼簽名過的,這代表若用戶端有修改這些值,就會讓 Cookie 變成無效。若要從 Request 中取得 Cookie,請使用 Illuminate\Http\Request 實體上的 cookie 方法:

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

輸入修剪與正常化

預設情況下,Laravel 中包含了 App\Http\Middleware\TrimStringsIlluminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull 這兩個 Middleware,且放在程式的全域 Middleware Stack 中。這些 Middleware 被列在 App\Http\Kernel 類別的全域 Middleware Stack 中。這些 Middleware 會自動修剪 Request 中的所有連入子船,並將空白的字串欄位轉為 null。這樣,我們就不需要在 Route 或 Controller 中去費心正常化這些資料。

禁用輸入正規化

若想在所有 Request 上禁用這些行為,可在 App\Http\Kernel 類別的 $middleware 屬性中將其移除:

若只想在專案中一部分的 Request 上禁用字串修剪與空字串轉換,可使用這兩個 Middleware 提供的 skipWhen 方法。請傳入一個回傳 truefalse 的閉包給該方法,用來判斷是否應跳過字串的正規化。一般來說,應在專案的 AppServiceProviderboot 方法內叫用這個 skipWhen 方法。

1use App\Http\Middleware\TrimStrings;
2use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
3 
4/**
5 * Bootstrap any application services.
6 *
7 * @return void
8 */
9public function boot()
10{
11 TrimStrings::skipWhen(function ($request) {
12 return $request->is('admin/*');
13 });
14 
15 ConvertEmptyStringsToNull::skipWhen(function ($request) {
16 // ...
17 });
18}
1use App\Http\Middleware\TrimStrings;
2use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
3 
4/**
5 * Bootstrap any application services.
6 *
7 * @return void
8 */
9public function boot()
10{
11 TrimStrings::skipWhen(function ($request) {
12 return $request->is('admin/*');
13 });
14 
15 ConvertEmptyStringsToNull::skipWhen(function ($request) {
16 // ...
17 });
18}

檔案

取得上傳的檔案

可以使用 file 方法或動態屬性來從 Illuminate\Http\Request 實體上取得上傳的檔案。file 方法會回傳 Illuminate\Http\UploadedFile 類別的實體,該實體繼承了 PHP 的 SplFileInfo 類別,並提供各種能處理使用該檔案的方法:

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

可以使用 hasFile 方法來判斷某個檔案是否存在於 Request 中:

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

驗證成功上傳

除了檢查檔案是否存在外,還可以使用 isValid 方法來確認上傳檔案的過程中是否無問題:

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

檔案路徑與副檔名

UploadedFile 類別也包含了能存取檔案完整路徑與副檔名的方法。extension 方法可以使用檔案的內容來推測檔案的副檔名。這個副檔名克呢功能會與用戶端提供的副檔名有所不同:

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

其他檔案方法

UploadedFile 實體還提供了其他各種方法。請參考該類別的 API 說明文件來瞭解有關這些方法的更多資訊。

儲存上傳的檔案

若要儲存已上傳的檔案,通常我們需要先設定好檔案系統UploadedFile 類別中有個 store 方法,該方法可以將已上傳的檔案移到其中一個磁碟裡。這個磁碟可以是本機檔案系統,也可以是像 Amazon S3 之類的雲端儲存空間。

store 方法接受一個路徑,該路徑就是相對於檔案系統設定中根目錄的位置。路徑不包含檔案名稱,Laravel 會自動產生獨立的 ID 來當作檔案名稱。

store 方法也接受可選的第二個引數,該引數是要用來儲存檔案的磁碟名稱。 store 方法會回傳相對於磁碟根目錄的檔案路徑:

1$path = $request->photo->store('images');
2 
3$path = $request->photo->store('images', 's3');
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');
1$path = $request->photo->storeAs('images', 'filename.jpg');
2 
3$path = $request->photo->storeAs('images', 'filename.jpg', 's3');
lightbulb

更多有關 Laravel 中檔案儲存的資訊,請參考完整的檔案儲存說明文件

設定信任的代理 (Trusted Proxy)

在負責處理 TLS / SSL 證書的 Load Balancer (負載平衡器) 後方執行應用程式時,有時候由 url 輔助函式產生的連結可能不會使用 HTTPS。者通常是因為,Load Balancer 把流量傳過來時使用的是 80 Port,因為 Laravel 不知道是否要產生 HTTPS 的連結。

為此,我們可以使用 Laravel 專案中有包含的 App\Http\Middleware\TrustProxies Middleware。該 Middleware 能讓我們快速自訂程式要信任的 Load Balancer 或代理伺服器 (Proxy)。應在該 Middleware 內的 $proxies 屬性內列出信任的代理伺服器。除了設定信任的代理外,也可以設定信任代理的 $headers

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Illuminate\Http\Middleware\TrustProxies as Middleware;
6use Illuminate\Http\Request;
7 
8class TrustProxies extends Middleware
9{
10 /**
11 * The trusted proxies for this application.
12 *
13 * @var string|array
14 */
15 protected $proxies = [
16 '192.168.1.1',
17 '192.168.1.2',
18 ];
19 
20 /**
21 * The headers that should be used to detect proxies.
22 *
23 * @var int
24 */
25 protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO;
26}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Illuminate\Http\Middleware\TrustProxies as Middleware;
6use Illuminate\Http\Request;
7 
8class TrustProxies extends Middleware
9{
10 /**
11 * The trusted proxies for this application.
12 *
13 * @var string|array
14 */
15 protected $proxies = [
16 '192.168.1.1',
17 '192.168.1.2',
18 ];
19 
20 /**
21 * The headers that should be used to detect proxies.
22 *
23 * @var int
24 */
25 protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO;
26}
lightbulb

若要使用 AWS Elastic Load Balancing,則 $headers 的值應為 Request::HEADER_X_FORWARDED_AWS_ELB。更多有關能用在 $headers 屬性的常數資訊,請參考 Symfony 說明文件中的 Trusting Proxies

信任所有代理

若使用 Amazon AWS 或其他的「雲端」Load Balancer 提供者,則我們可能不知道 Load Balancer 實際的 IP 位置。這時,可以使用 * 來信任所有代理:

1/**
2 * The trusted proxies for this application.
3 *
4 * @var string|array
5 */
6protected $proxies = '*';
1/**
2 * The trusted proxies for this application.
3 *
4 * @var string|array
5 */
6protected $proxies = '*';

設定信任的主機 (Trusted Hosts)

預設情況下,無論收到的 HTTP Request 中 Host 標頭內容為何,Laravel 都會回應所有收到的 Request。此外,Laravel 還會使用 Host 標頭的值來在 Request 中為你的程式產生絕對路徑的網址。

一般來說,應在 Web Server 上 (如 Nginx 或 Apache) 設定只有特定的主機名稱時才將 Request 送往你的程式中。不過,若沒機會能自訂 Web Server,則需要讓 Laravel 只對特定主機名稱作回應。為此,可以啟用專案中的 App\Http\Middleware\TrustHosts Middleware。

TrustHosts Middleware 以預先包含在專案中的 $middlware Stack 裡的。不過,需要先取消註解這個 Middleware,才能啟用它。在這個 Middleware 中有個 hosts 方法,我們可以在其中指定我們的程式要回應的主機名稱。有其他 Host 標頭值的連入 Request 將會被拒絕:

1/**
2 * Get the host patterns that should be trusted.
3 *
4 * @return array
5 */
6public function hosts()
7{
8 return [
9 'laravel.test',
10 $this->allSubdomainsOfApplicationUrl(),
11 ];
12}
1/**
2 * Get the host patterns that should be trusted.
3 *
4 * @return array
5 */
6public function hosts()
7{
8 return [
9 'laravel.test',
10 $this->allSubdomainsOfApplicationUrl(),
11 ];
12}

allSubdomainsOfApplicationUrl 輔助函式會回傳一個可配對應用程式中 app.url 設定值子網域的正規表示式。使用這個輔助函式,就可以方便地在使用萬用子網域的程式中允許所有的子網域。

翻譯進度
100% 已翻譯
更新時間:
2023年2月11日 上午10:28:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.