HTTP Response

建立 Response

字串與陣列

所有的 Route 與 Controller 都應回傳 Response,以傳送回使用者的瀏覽器。Laravel 中提供了多種不同的方法來回傳 Response。最基礎的 Response 就是從 Route 或 Controller 中回傳字串。Laravel 會自動將字串轉換為完整的 HTTP 回應:

1Route::get('/', function () {
2 return 'Hello World';
3});
1Route::get('/', function () {
2 return 'Hello World';
3});

除了從 Route 與 Controller 中回傳字串外,也可以回傳陣列。Laravel 會自動將陣列轉換為 JSON 回應:

1Route::get('/', function () {
2 return [1, 2, 3];
3});
1Route::get('/', function () {
2 return [1, 2, 3];
3});
lightbulb

你知道你也可以從 Route 或 Controller 中回傳 Eloquent Collection 嗎?回傳的 Eloquent Collection 會自動被轉為 JSON。試試看吧!

Response 物件

通常來說,我們不會只想在 Route 動作裡回傳簡單的字串或陣列。除了字串 / 陣列外,我們還可以回傳完整的 Illuminate\Http\Response 實體或 View

若回傳完整的 Response,就可以自訂回應的 HTTP 狀態碼與標頭。Response 實體繼承自 Symfony\Component\HttpFoundation\Response 類別,該類別提供各種不同的方法來建立 HTTP Response:

1Route::get('/home', function () {
2 return response('Hello World', 200)
3 ->header('Content-Type', 'text/plain');
4});
1Route::get('/home', function () {
2 return response('Hello World', 200)
3 ->header('Content-Type', 'text/plain');
4});

Eloquent Model 與 Collection

我們也可以從 Route 或 Controller 中回傳 Eloquent ORM Model 或 Collection。回傳 Eloquent Model 或 Collection 時,Laravel 會自動將其轉換為 JSON 回應。當 Model 上有隱藏屬性時,這些屬性也會被隱藏:

1use App\Models\User;
2 
3Route::get('/user/{user}', function (User $user) {
4 return $user;
5});
1use App\Models\User;
2 
3Route::get('/user/{user}', function (User $user) {
4 return $user;
5});

將標頭附加到 Response

請記得,大多數的 Response 方法都是可串連的 (Chainable),讓我們能流暢地建構 Response 實體。舉例來說,我們可以在把 Respnse 傳回給使用者前使用 header 方法來加上一系列的標頭:

1return response($content)
2 ->header('Content-Type', $type)
3 ->header('X-Header-One', 'Header Value')
4 ->header('X-Header-Two', 'Header Value');
1return response($content)
2 ->header('Content-Type', $type)
3 ->header('X-Header-One', 'Header Value')
4 ->header('X-Header-Two', 'Header Value');

或者,我們也可以使用 withHeaders 方法來指定一組包含標頭的陣列,來將該陣列加到 Response 上:

1return response($content)
2 ->withHeaders([
3 'Content-Type' => $type,
4 'X-Header-One' => 'Header Value',
5 'X-Header-Two' => 'Header Value',
6 ]);
1return response($content)
2 ->withHeaders([
3 'Content-Type' => $type,
4 'X-Header-One' => 'Header Value',
5 'X-Header-Two' => 'Header Value',
6 ]);

快取 Controller Middleware

Laravel 中提供了一個 cache.headers Middleware,可以使用該 Middleware 來快速將 Cache-Control 標頭設定到一組 Route 上。必須提供與 Cache-Control 指示詞 (Directive) 對應的「蛇形命名法 (snake_case)」指示詞,並使用分號區隔。若指示詞列表中有 etag,則會自動以 Response 內容的 MD5 雜湊 (Hash) 來設定 ETag 識別元:

1Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () {
2 Route::get('/privacy', function () {
3 // ...
4 });
5 
6 Route::get('/terms', function () {
7 // ...
8 });
9});
1Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () {
2 Route::get('/privacy', function () {
3 // ...
4 });
5 
6 Route::get('/terms', function () {
7 // ...
8 });
9});

附加 Cookie 到 Response

可以使用 cookie 方法來將 Cookie 附加到外連的 Illuminate\Http\Response 實體。我們可以傳入 Cookie 的名稱、Cookie 值、以及單位為分鐘的有效期限給該方法:

1return response('Hello World')->cookie(
2 'name', 'value', $minutes
3);
1return response('Hello World')->cookie(
2 'name', 'value', $minutes
3);

cookie 方法還接受一些更多的引數,但這些引數很少用。一般來說,這些引數的功能跟 PHP 原生的 setcookie 方法一樣:

1return response('Hello World')->cookie(
2 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly
3);
1return response('Hello World')->cookie(
2 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly
3);

若想要與連出的 Response 一起送出 Cookie,但目前還未有 Response 實體的話,可使用 Cookie Facade 來將 Cookie 「放到佇列」,以在 Response 送出的時候將其附加上去。queue (佇列) 方法接受要用來建立 Cookie 實體的引數。這些佇列中的 Cookie 會在連出 Response 被送到瀏覽器前被附加上去:

1use Illuminate\Support\Facades\Cookie;
2 
3Cookie::queue('name', 'value', $minutes);
1use Illuminate\Support\Facades\Cookie;
2 
3Cookie::queue('name', 'value', $minutes);

若想產生稍後可附加到 Response 實體上的 Symfony\Component\HttpFoundation\Cookie 實體,則可使用全域的 cookie 輔助函式。必須將產生的 Cookie 附加到 Response 實體,這些 Cookie 才會被送回用戶端:

1$cookie = cookie('name', 'value', $minutes);
2 
3return response('Hello World')->cookie($cookie);
1$cookie = cookie('name', 'value', $minutes);
2 
3return response('Hello World')->cookie($cookie);

提早讓 Cookie 過期

可以在連外 Response 上使用 withoutCookie 方法來讓 Cookie 無效,以將 Cookie 移除:

1return response('Hello World')->withoutCookie('name');
1return response('Hello World')->withoutCookie('name');

若還未有連外 Response 實體,則可以使用 Cookie Facade 的 expire 方法來讓 Cookie 過期:

1Cookie::expire('name');
1Cookie::expire('name');

Cookie 與加密

預設情況下,Laravel 產生的所有 Cookie 都經過加密並簽名,因此這些 Cookie 在用戶端上都無法被修改或讀取。若想讓你的程式中一部分的 Cookie 不啟用加密,則可使用 App\Http\Middleware\EncryptCookies Middleware 的 $except 屬性,這個 Middleware 位在 app/Http/Middleware 目錄:

1/**
2 * The names of the cookies that should not be encrypted.
3 *
4 * @var array
5 */
6protected $except = [
7 'cookie_name',
8];
1/**
2 * The names of the cookies that should not be encrypted.
3 *
4 * @var array
5 */
6protected $except = [
7 'cookie_name',
8];

重新導向

Redirect Response (重新導向回應) 是 Illuminate\Http\RedirectResponse 類別的實體,Redirect Response 中包含了用來將使用者重新導向到另一個網址所需的一些標頭 (Header)。要產生 RedirectResponse 實體有幾個方法。最簡單的方法是使用全域的 redirect 輔助函式:

1Route::get('/dashboard', function () {
2 return redirect('home/dashboard');
3});
1Route::get('/dashboard', function () {
2 return redirect('home/dashboard');
3});

有時候(如:使用者送出了無效的表單時),我們可能會想把使用者重新導向到使用者瀏覽的前一個位置。為此,我們可以使用全域的 back 輔助函式。由於這個功能使用了 Session,因此請確保呼叫 back 函式的 Route 有使用 web Middleware 群組:

1Route::post('/user/profile', function () {
2 // 驗證 Request...
3 
4 return back()->withInput();
5});
1Route::post('/user/profile', function () {
2 // 驗證 Request...
3 
4 return back()->withInput();
5});

重新導向到命名 Route

呼叫 redirect 輔助函式時若沒有帶上任何參數,則會回傳 Illuminate\Routing\Redirector 實體,這樣我們就可以呼叫 Redirect 實體上的所有方法。舉例來說,若要為某個命名 Route 產生 RedirectResponse,可以使用 route 方法:

1return redirect()->route('login');
1return redirect()->route('login');

若 Route 有參數,則可將這些 Route 參數作為第二個引數傳給 route 方法:

1// 給有下列 URI 的 Route:/profile/{id}
2 
3return redirect()->route('profile', ['id' => 1]);
1// 給有下列 URI 的 Route:/profile/{id}
2 
3return redirect()->route('profile', ['id' => 1]);

使用 Eloquent Model 來填充參數

若要重新導向的 Route 中有個可從 Eloquent Model 中填充的「ID」參數,則可傳入 Model。會自動取出 ID:

1// 給有下列 URI 的 Route:/profile/{id}
2 
3return redirect()->route('profile', [$user]);
1// 給有下列 URI 的 Route:/profile/{id}
2 
3return redirect()->route('profile', [$user]);

若想自訂放在 Route 參數中的值,可在 Route 的參數定義中指定欄位 (/profile/{id:slug}),或是在 Eloquent Model 中複寫 getRouteKey 方法:

1/**
2 * Get the value of the model's route key.
3 */
4public function getRouteKey(): mixed
5{
6 return $this->slug;
7}
1/**
2 * Get the value of the model's route key.
3 */
4public function getRouteKey(): mixed
5{
6 return $this->slug;
7}

重新導向到 Controller 動作

也可以產生一個前往 Controller 動作的重新導向。為此,請將 Controller 與動作名稱傳入 action 方法:

1use App\Http\Controllers\UserController;
2 
3return redirect()->action([UserController::class, 'index']);
1use App\Http\Controllers\UserController;
2 
3return redirect()->action([UserController::class, 'index']);

若這個 Controller 的 Route 有要求參數,則可將這些參數作為第二個引數傳給 action 方法:

1return redirect()->action(
2 [UserController::class, 'profile'], ['id' => 1]
3);
1return redirect()->action(
2 [UserController::class, 'profile'], ['id' => 1]
3);

重新導向到外部網域

有時候,我們會需要重新導向到程式外部的網域。為此,可以呼叫 away 方法。該方法會建立一個 RedirectResponse,並且不會做額外的 URL 編碼或驗證:

1return redirect()->away('https://www.google.com');
1return redirect()->away('https://www.google.com');

重新導向時帶上快閃存入的 Session 資料

通常,我們在重新導向到新網址的時候,也會[將資料快閃存入 Session]。一般來說,這種情況通常是當某個動作順利進行,而我們將成功訊息寫入 Session 時。為了方便起見,我們可以建立一個 RedirectResponse 實體,並以一行流暢的方法串連呼叫來將資料快閃存入 Session:

1Route::post('/user/profile', function () {
2 // ...
3 
4 return redirect('dashboard')->with('status', 'Profile updated!');
5});
1Route::post('/user/profile', function () {
2 // ...
3 
4 return redirect('dashboard')->with('status', 'Profile updated!');
5});

使用者被重新導向後,我們就可以從 Session 中顯示出剛才快閃存入的資料。舉例來說,我們可以使用 Blade 語法

1@if (session('status'))
2 <div class="alert alert-success">
3 {{ session('status') }}
4 </div>
5@endif
1@if (session('status'))
2 <div class="alert alert-success">
3 {{ session('status') }}
4 </div>
5@endif

重新導向時帶上輸入

可以使用 RedirectResponse 實體提供的 withInput 方法來在將使用者重新導向到新位置前先將目前 Request 的輸入資料快閃存入 Session 中。通常來說我們會在表單驗證錯誤時這麼做。將輸入資料快閃存入 Session 後,我們就可以在下一個 Request 中輕鬆地取得這些資料並將其填回表單中:

1return back()->withInput();
1return back()->withInput();

其他 Response 類型

response 輔助函式還能產生一些其他類型的 Response 實體。呼叫 response 輔助函式時若未帶入任何引數,則會回傳 Illuminate\Contracts\Routing\ResponseFactory Contract 的實作。這個 Contract 提供了數種用來建立 Response 的實用方法:

View Response

若有需要控制 Response 的狀態與標頭,但 Response 的內容又需要是 [View] 時,則可使用 view 方法:

1return response()
2 ->view('hello', $data, 200)
3 ->header('Content-Type', $type);
1return response()
2 ->view('hello', $data, 200)
3 ->header('Content-Type', $type);

當然,若不需要傳入自訂 HTTP 狀態或自訂標頭的話,應該使用全域的 view 輔助函式。

JSON Response

json 方法會自動將 Content-Type 標頭設為 application/json,並使用 json_encode PHP 函式來將任何給定的陣列轉換為 JSON:

1return response()->json([
2 'name' => 'Abigail',
3 'state' => 'CA',
4]);
1return response()->json([
2 'name' => 'Abigail',
3 'state' => 'CA',
4]);

若想建立 JSONP Response,則可在使用 json 方法時搭配使用 withCallback 方法:

1return response()
2 ->json(['name' => 'Abigail', 'state' => 'CA'])
3 ->withCallback($request->input('callback'));
1return response()
2 ->json(['name' => 'Abigail', 'state' => 'CA'])
3 ->withCallback($request->input('callback'));

檔案下載

可使用 download 方法來產生一個強制使用者在給定路徑上下載檔案的 Response。download 方法接受檔案名稱作為其第二個引數,該引數用來判斷使用者看到的檔案名稱。最後,我們可以傳入一組包含 HTTP 標頭的陣列作為該方法的第三個引數:

1return response()->download($pathToFile);
2 
3return response()->download($pathToFile, $name, $headers);
1return response()->download($pathToFile);
2 
3return response()->download($pathToFile, $name, $headers);
exclamation

Symfony HttpFoundation —— 負責處理檔案下載的類別 —— 要求下載的檔案名稱必須為 ASCII。

串流下載

有時候,我們會需要在不寫入磁碟的情況下將某個操作的字串結果轉變成可下載的 Response。這時可以使用 streamDownload 方法。這個方法接受一個回呼、檔案名稱、以及一個可選的標頭陣列作為其引數:

1use App\Services\GitHub;
2 
3return response()->streamDownload(function () {
4 echo GitHub::api('repo')
5 ->contents()
6 ->readme('laravel', 'laravel')['contents'];
7}, 'laravel-readme.md');
1use App\Services\GitHub;
2 
3return response()->streamDownload(function () {
4 echo GitHub::api('repo')
5 ->contents()
6 ->readme('laravel', 'laravel')['contents'];
7}, 'laravel-readme.md');

File Response

比起直接讓使用者下載檔案,我們可以使用 file 方法來直接將檔案(如圖片或 PDF)顯示在使用者的瀏覽器上。這個方法接受檔案路徑作為其第一個引數,以及一個包含標頭的陣列作為其第二個引數:

1return response()->file($pathToFile);
2 
3return response()->file($pathToFile, $headers);
1return response()->file($pathToFile);
2 
3return response()->file($pathToFile, $headers);

Response Macro

若想定義可在各個 Route 或 Controller 內重複使用的自訂 Response 方法,可使用 Response Facade 上的 macro 方法。通常來說,該方法應在某個 Service Providerboot 方法內呼叫,如 App\Providers\AppServiceProvider Service Provider:

1<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Support\Facades\Response;
6use Illuminate\Support\ServiceProvider;
7 
8class AppServiceProvider extends ServiceProvider
9{
10 /**
11 * Bootstrap any application services.
12 */
13 public function boot(): void
14 {
15 Response::macro('caps', function (string $value) {
16 return Response::make(strtoupper($value));
17 });
18 }
19}
1<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Support\Facades\Response;
6use Illuminate\Support\ServiceProvider;
7 
8class AppServiceProvider extends ServiceProvider
9{
10 /**
11 * Bootstrap any application services.
12 */
13 public function boot(): void
14 {
15 Response::macro('caps', function (string $value) {
16 return Response::make(strtoupper($value));
17 });
18 }
19}

macro 方法接受一個名稱作為其第一個引數,以及閉包作為其第二個引數。當在 ResponseFactory 的實作或 response 輔助函式上呼叫給定的 Macro 名稱時,會執行該 Macro 的閉包:

1return response()->caps('foo');
1return response()->caps('foo');
翻譯進度
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.