HTTP 響應
建立響應
字串與陣列
所有的路由和控制器都應該返回一個響應傳送回用戶的瀏覽器。Laravel 提供了多種不同的方式來返回響應。最基本的響應是從路由或控制器返回一個字串。框架會自動將該字串轉換為完整的 HTTP 響應。
1Route::get('/', function () {2 return 'Hello World';3});
除了從路由和控制器返回字串外,你也可以返回陣列。框架會自動將陣列轉換為 JSON 響應。
1Route::get('/', function () {2 return [1, 2, 3];3});
你知道嗎?你還可以從路由或控制器中返回 Eloquent 集合。它們會被自動轉換為 JSON。試一試吧!
響應物件
通常,你不僅僅會從路由動作中返回簡單的字串或陣列,而是返回完整的 Illuminate\Http\Response 例項或 檢視。
返回一個完整的 Response 例項允許你自定義響應的 HTTP 狀態碼和響應頭。Response 例項繼承自 Symfony\Component\HttpFoundation\Response 類,該類提供了多種用於構建 HTTP 響應的方法。
1Route::get('/home', function () {2 return response('Hello World', 200)3 ->header('Content-Type', 'text/plain');4});
Eloquent 模型與集合
你也可以直接從路由和控制器返回 Eloquent ORM 模型和集合。當你這樣做時,Laravel 會在遵守模型 隱藏屬性 的前提下,自動將模型和集合轉換為 JSON 響應。
1use App\Models\User;2 3Route::get('/user/{user}', function (User $user) {4 return $user;5});
向響應新增響應頭
請記住,大多數響應方法都是可以鏈式呼叫的,這允許你流暢地構建響應例項。例如,你可以在將響應傳送回用戶之前,使用 header 方法新增一系列響應頭。
1return response($content)2 ->header('Content-Type', $type)3 ->header('X-Header-One', 'Header Value')4 ->header('X-Header-Two', 'Header Value');
或者,你可以使用 withHeaders 方法指定一個數組,將其作為響應頭新增到響應中。
1return response($content)2 ->withHeaders([3 'Content-Type' => $type,4 'X-Header-One' => 'Header Value',5 'X-Header-Two' => 'Header Value',6 ]);
你可以使用 withoutHeader 方法從外發響應中移除指定的響應頭。
1return response($content)->withoutHeader('X-Debug');2 3return response($content)->withoutHeader(['X-Debug', 'X-Powered-By']);
快取控制中介軟體
Laravel 包含一個 cache.headers 中介軟體,可用於快速為一組路由設定 Cache-Control 響應頭。指令應使用對應 Cache-Control 指令的“蛇形命名法(snake case)”等效項,並以分號分隔。如果指令列表中指定了 etag,則響應內容的 MD5 雜湊值將自動設定為 ETag 識別符號。
1Route::middleware('cache.headers:public;max_age=30;s_maxage=300;stale_while_revalidate=600;etag')->group(function () {2 Route::get('/privacy', function () {3 // ...4 });5 6 Route::get('/terms', function () {7 // ...8 });9});
向響應新增 Cookie
你可以使用 cookie 方法將 Cookie 新增到外發的 Illuminate\Http\Response 例項中。你應該向此方法傳遞名稱、值以及 Cookie 有效的分鐘數。
1return response('Hello World')->cookie(2 'name', 'value', $minutes3);
cookie 方法還接受一些不太常用的附加引數。通常,這些引數的作用和意義與 PHP 原生 setcookie 方法的引數相同。
1return response('Hello World')->cookie(2 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly3);
如果你希望確保 Cookie 隨外發響應傳送,但你還沒有該響應的例項,你可以使用 Cookie 門面(facade)來“排隊”等待發送響應時附加這些 Cookie。queue 方法接受建立 Cookie 例項所需的引數。這些 Cookie 將在傳送到瀏覽器之前被附加到外發響應中。
1use Illuminate\Support\Facades\Cookie;2 3Cookie::queue('name', 'value', $minutes);
生成 Cookie 例項
如果你想生成一個 Symfony\Component\HttpFoundation\Cookie 例項,以便稍後附加到響應例項中,你可以使用全域性的 cookie 輔助函式。除非將其附加到響應例項,否則此 Cookie 不會被髮送回客戶端。
1$cookie = cookie('name', 'value', $minutes);2 3return response('Hello World')->cookie($cookie);
提前讓 Cookie 過期
你可以透過外發響應的 withoutCookie 方法移除一個 Cookie,使其過期。
1return response('Hello World')->withoutCookie('name');
如果你還沒有外發響應的例項,你可以使用 Cookie 門面的 expire 方法讓 Cookie 過期。
1Cookie::expire('name');
Cookie 與加密
預設情況下,由於 Illuminate\Cookie\Middleware\EncryptCookies 中介軟體的存在,Laravel 生成的所有 Cookie 都會經過加密和簽名,因此客戶端無法對其進行修改或讀取。如果你想為應用程式生成的某些 Cookie 停用加密,可以在應用程式的 bootstrap/app.php 檔案中使用 encryptCookies 方法。
1->withMiddleware(function (Middleware $middleware): void {2 $middleware->encryptCookies(except: [3 'cookie_name',4 ]);5})
通常情況下,永遠不應停用 Cookie 加密,因為這會將你的 Cookie 暴露在潛在的客戶端資料洩露和篡改風險中。
重定向
重定向響應是 Illuminate\Http\RedirectResponse 類的例項,包含將使用者重定向到另一個 URL 所需的正確響應頭。生成 RedirectResponse 例項有多種方法,最簡單的方法是使用全域性 redirect 輔助函式。
1Route::get('/dashboard', function () {2 return redirect('/home/dashboard');3});
有時你可能希望將使用者重定向回上一個位置,例如在提交的表單無效時。你可以使用全域性的 back 輔助函式來實現。由於此功能利用了 session,請確保呼叫 back 函式的路由使用了 web 中介軟體組。
1Route::post('/user/profile', function () {2 // Validate the request...3 4 return back()->withInput();5});
重定向到命名路由
當你呼叫不帶引數的 redirect 輔助函式時,會返回一個 Illuminate\Routing\Redirector 例項,允許你在 Redirector 例項上呼叫任何方法。例如,要生成指向命名路由的 RedirectResponse,可以使用 route 方法。
1return redirect()->route('login');
如果你的路由包含引數,可以將它們作為第二個引數傳遞給 route 方法。
1// For a route with the following URI: /profile/{id}2 3return redirect()->route('profile', ['id' => 1]);
透過 Eloquent 模型填充引數
如果你要重定向到的路由包含一個從 Eloquent 模型填充的“ID”引數,你可以直接傳遞該模型例項。ID 將會自動提取。
1// For a route with the following URI: /profile/{id}2 3return redirect()->route('profile', [$user]);
如果你想自定義路由引數中使用的值,可以在路由引數定義中指定列名(例如 /profile/{id:slug}),或者在 Eloquent 模型中重寫 getRouteKey 方法。
1/**2 * Get the value of the model's route key.3 */4public function getRouteKey(): mixed5{6 return $this->slug;7}
重定向到控制器動作
你還可以生成指向 控制器動作 的重定向。為此,請將控制器名稱和動作名稱傳遞給 action 方法。
1use App\Http\Controllers\UserController;2 3return redirect()->action([UserController::class, 'index']);
如果你的控制器路由需要引數,可以將它們作為第二個引數傳遞給 action 方法。
1return redirect()->action(2 [UserController::class, 'profile'], ['id' => 1]3);
重定向到外部域名
有時你可能需要重定向到應用程式之外的域名。你可以透過呼叫 away 方法來實現,它會建立一個不進行任何額外 URL 編碼、驗證或檢查的 RedirectResponse。
1return redirect()->away('https://www.google.com');
帶快閃記憶體會話資料的重定向
重定向到新 URL 和 將資料快閃記憶體到 session 通常同時進行。這通常在成功執行某個操作後完成,例如將成功訊息快閃記憶體到 session。為了方便起見,你可以在一個流暢的方法鏈中建立一個 RedirectResponse 例項並將資料快閃記憶體到 session。
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
重定向並攜帶輸入資料
你可以使用 RedirectResponse 例項提供的 withInput 方法,在重定向使用者到新位置之前,將當前請求的輸入資料快閃記憶體到 session。這通常在使用者遇到驗證錯誤時使用。一旦輸入資料被快閃記憶體到 session,你就可以在下一次請求時輕鬆地 檢索它 以重新填充表單。
1return back()->withInput();
其他響應型別
response 輔助函式可用於生成其他型別的響應例項。當不帶引數呼叫 response 輔助函式時,會返回一個實現了 Illuminate\Contracts\Routing\ResponseFactory 契約 的例項。該契約提供了多種用於生成響應的有用方法。
檢視響應
如果你需要控制響應的狀態碼和響應頭,同時又需要將 檢視 作為響應內容返回,你應該使用 view 方法。
1return response()2 ->view('hello', $data, 200)3 ->header('Content-Type', $type);
當然,如果你不需要傳遞自定義的 HTTP 狀態碼或自定義響應頭,可以直接使用全域性 view 輔助函式。
JSON 響應
json 方法會自動將 Content-Type 響應頭設定為 application/json,並使用 PHP 的 json_encode 函式將給定的陣列轉換為 JSON。
1return response()->json([2 'name' => 'Abigail',3 'state' => 'CA',4]);
如果你想建立一個 JSONP 響應,可以將 json 方法與 withCallback 方法結合使用。
1return response()2 ->json(['name' => 'Abigail', 'state' => 'CA'])3 ->withCallback($request->input('callback'));
檔案下載
download 方法可用於生成強制使用者瀏覽器下載指定路徑檔案的響應。download 方法接受檔名作為第二個引數,該引數將決定使用者下載檔案時看到的檔名。最後,你可以將 HTTP 響應頭陣列作為第三個引數傳遞給該方法。
1return response()->download($pathToFile);2 3return response()->download($pathToFile, $name, $headers);
管理檔案下載的 Symfony HttpFoundation 要求被下載的檔名必須是 ASCII 編碼的。
檔案響應
file 方法可用於直接在使用者瀏覽器中顯示檔案(例如圖片或 PDF),而不是觸發下載。此方法接受檔案的絕對路徑作為第一個引數,響應頭陣列作為第二個引數。
1return response()->file($pathToFile);2 3return response()->file($pathToFile, $headers);
流式響應
透過在資料生成時將其流式傳輸給客戶端,你可以顯著減少記憶體佔用並提高效能,特別是在處理非常大的響應時。流式響應允許客戶端在伺服器完成傳送之前就開始處理資料。
1Route::get('/stream', function () { 2 return response()->stream(function (): void { 3 foreach (['developer', 'admin'] as $string) { 4 echo $string; 5 ob_flush(); 6 flush(); 7 sleep(2); // Simulate delay between chunks... 8 } 9 }, 200, ['X-Accel-Buffering' => 'no']);10});
為了方便起見,如果你傳遞給 stream 方法的閉包返回一個 生成器(Generator),Laravel 將自動在生成器返回的字串之間重新整理輸出緩衝區,並停用 Nginx 輸出緩衝。
1Route::post('/chat', function () {2 return response()->stream(function (): Generator {3 $stream = OpenAI::client()->chat()->createStreamed(...);4 5 foreach ($stream as $response) {6 yield $response->choices[0];7 }8 });9});
消費流式響應
流式響應可以使用 Laravel 的 stream npm 包來消費,該包為與 Laravel 響應和事件流互動提供了便捷的 API。要開始使用,請安裝 @laravel/stream-react、@laravel/stream-vue 或 @laravel/stream-svelte 包。
1npm install @laravel/stream-react
1npm install @laravel/stream-vue
1npm install @laravel/stream-svelte
然後,可以使用 useStream 來消費事件流。在提供流 URL 後,該 Hook 會隨著 Laravel 應用程式返回內容,自動更新 data 為拼接後的響應。
1import { useStream } from "@laravel/stream-react"; 2 3function App() { 4 const { data, isFetching, isStreaming, send } = useStream("chat"); 5 6 const sendMessage = () => { 7 send({ 8 message: `Current timestamp: ${Date.now()}`, 9 });10 };11 12 return (13 <div>14 <div>{data}</div>15 {isFetching && <div>Connecting...</div>}16 {isStreaming && <div>Generating...</div>}17 <button onClick={sendMessage}>Send Message</button>18 </div>19 );20}
1<script setup lang="ts"> 2import { useStream } from "@laravel/stream-vue"; 3 4const { data, isFetching, isStreaming, send } = useStream("chat"); 5 6const sendMessage = () => { 7 send({ 8 message: `Current timestamp: ${Date.now()}`, 9 });10};11</script>12 13<template>14 <div>15 <div>{{ data }}</div>16 <div v-if="isFetching">Connecting...</div>17 <div v-if="isStreaming">Generating...</div>18 <button @click="sendMessage">Send Message</button>19 </div>20</template>
<script>import { useStream } from "@laravel/stream-svelte";const stream = useStream("chat");const sendMessage = () => { stream.send({ message: `Current timestamp: ${Date.now()}`, });};</script><div> <div>{$stream.data}</div> {#if $stream.isFetching} <div>Connecting...</div> {/if} {#if $stream.isStreaming} <div>Generating...</div> {/if} <button onclick={sendMessage}>Send Message</button></div>
當透過 send 將資料傳回流時,活動連線會在傳送新資料之前取消。所有請求都作為 JSON POST 請求傳送。
由於 useStream Hook 會向你的應用程式發起 POST 請求,因此需要有效的 CSRF 令牌。提供 CSRF 令牌最簡單的方法是 將其包含在應用程式佈局的 head 標籤中的 meta 標籤裡。
傳遞給 useStream 的第二個引數是一個選項物件,你可以用它來自定義流消費行為。該物件的預設值如下所示:
1import { useStream } from "@laravel/stream-react"; 2 3function App() { 4 const { data } = useStream("chat", { 5 id: undefined, 6 initialInput: undefined, 7 headers: undefined, 8 csrfToken: undefined, 9 onResponse: (response: Response) => void,10 onData: (data: string) => void,11 onCancel: () => void,12 onFinish: () => void,13 onError: (error: Error) => void,14 });15 16 return <div>{data}</div>;17}
1<script setup lang="ts"> 2import { useStream } from "@laravel/stream-vue"; 3 4const { data } = useStream("chat", { 5 id: undefined, 6 initialInput: undefined, 7 headers: undefined, 8 csrfToken: undefined, 9 onResponse: (response: Response) => void,10 onData: (data: string) => void,11 onCancel: () => void,12 onFinish: () => void,13 onError: (error: Error) => void,14});15</script>16 17<template>18 <div>{{ data }}</div>19</template>
1<script> 2import { useStream } from "@laravel/stream-svelte"; 3 4const stream = useStream("chat", { 5 id: undefined, 6 initialInput: undefined, 7 headers: undefined, 8 csrfToken: undefined, 9 onResponse: (response) => {},10 onData: (data) => {},11 onCancel: () => {},12 onFinish: () => {},13 onError: (error) => {},14});15</script>16 17<div>{$stream.data}</div>
onResponse 在流成功獲得初始響應後觸發,原始 Response 會被傳遞給回撥函式。onData 在收到每個資料塊時被呼叫,當前資料塊會被傳遞給回撥函式。onFinish 在流結束時以及獲取/讀取週期中丟擲錯誤時被呼叫。
預設情況下,初始化時不會向流發出請求。你可以使用 initialInput 選項向流傳遞初始有效載荷。
1import { useStream } from "@laravel/stream-react"; 2 3function App() { 4 const { data } = useStream("chat", { 5 initialInput: { 6 message: "Introduce yourself.", 7 }, 8 }); 9 10 return <div>{data}</div>;11}
1<script setup lang="ts"> 2import { useStream } from "@laravel/stream-vue"; 3 4const { data } = useStream("chat", { 5 initialInput: { 6 message: "Introduce yourself.", 7 }, 8}); 9</script>10 11<template>12 <div>{{ data }}</div>13</template>
1<script> 2import { useStream } from "@laravel/stream-svelte"; 3 4const stream = useStream("chat", { 5 initialInput: { 6 message: "Introduce yourself.", 7 }, 8}); 9</script>10 11<div>{$stream.data}</div>
要手動取消流,你可以使用 Hook 返回的 cancel 方法。
1import { useStream } from "@laravel/stream-react"; 2 3function App() { 4 const { data, cancel } = useStream("chat"); 5 6 return ( 7 <div> 8 <div>{data}</div> 9 <button onClick={cancel}>Cancel</button>10 </div>11 );12}
1<script setup lang="ts"> 2import { useStream } from "@laravel/stream-vue"; 3 4const { data, cancel } = useStream("chat"); 5</script> 6 7<template> 8 <div> 9 <div>{{ data }}</div>10 <button @click="cancel">Cancel</button>11 </div>12</template>
1<script> 2import { useStream } from "@laravel/stream-svelte"; 3 4const stream = useStream("chat"); 5</script> 6 7<div> 8 <div>{$stream.data}</div> 9 <button onclick={() => stream.cancel()}>Cancel</button>10</div>
每次使用 useStream Hook 時,都會生成一個隨機的 id 來標識該流。該 ID 會在每個請求中透過 X-STREAM-ID 響應頭髮送回伺服器。當從多個元件消費同一個流時,你可以透過提供自己的 id 來讀取和寫入該流。
1// App.tsx 2import { useStream } from "@laravel/stream-react"; 3 4function App() { 5 const { data, id } = useStream("chat"); 6 7 return ( 8 <div> 9 <div>{data}</div>10 <StreamStatus id={id} />11 </div>12 );13}14 15// StreamStatus.tsx16import { useStream } from "@laravel/stream-react";17 18function StreamStatus({ id }) {19 const { isFetching, isStreaming } = useStream("chat", { id });20 21 return (22 <div>23 {isFetching && <div>Connecting...</div>}24 {isStreaming && <div>Generating...</div>}25 </div>26 );27}
1<!-- App.vue --> 2<script setup lang="ts"> 3import { useStream } from "@laravel/stream-vue"; 4import StreamStatus from "./StreamStatus.vue"; 5 6const { data, id } = useStream("chat"); 7</script> 8 9<template>10 <div>11 <div>{{ data }}</div>12 <StreamStatus :id="id" />13 </div>14</template>15 16<!-- StreamStatus.vue -->17<script setup lang="ts">18import { useStream } from "@laravel/stream-vue";19 20const props = defineProps<{21 id: string;22}>();23 24const { isFetching, isStreaming } = useStream("chat", { id: props.id });25</script>26 27<template>28 <div>29 <div v-if="isFetching">Connecting...</div>30 <div v-if="isStreaming">Generating...</div>31 </div>32</template>
<!-- App.svelte --><script>import { useStream } from "@laravel/stream-svelte";import StreamStatus from "./StreamStatus.svelte";const stream = useStream("chat");</script><div> <div>{$stream.data}</div> <StreamStatus id={stream.id} /></div><!-- StreamStatus.svelte --><script>import { useStream } from "@laravel/stream-svelte";let { id } = $props();const stream = useStream("chat", { id });</script><div> {#if $stream.isFetching} <div>Connecting...</div> {/if} {#if $stream.isStreaming} <div>Generating...</div> {/if}</div>
流式 JSON 響應
如果你需要增量地流式傳輸 JSON 資料,可以使用 streamJson 方法。此方法對於需要逐步傳送到瀏覽器並能被 JavaScript 輕鬆解析的大型資料集特別有用。
1use App\Models\User;2 3Route::get('/users.json', function () {4 return response()->streamJson([5 'users' => User::cursor(),6 ]);7});
useJsonStream Hook 與 useStream Hook 基本相同,區別在於它會在流傳輸完成後嘗試將資料解析為 JSON。
1import { useJsonStream } from "@laravel/stream-react"; 2 3type User = { 4 id: number; 5 name: string; 6 email: string; 7}; 8 9function App() {10 const { data, send } = useJsonStream<{ users: User[] }>("users");11 12 const loadUsers = () => {13 send({14 query: "taylor",15 });16 };17 18 return (19 <div>20 <ul>21 {data?.users.map((user) => (22 <li>23 {user.id}: {user.name}24 </li>25 ))}26 </ul>27 <button onClick={loadUsers}>Load Users</button>28 </div>29 );30}
1<script setup lang="ts"> 2import { useJsonStream } from "@laravel/stream-vue"; 3 4type User = { 5 id: number; 6 name: string; 7 email: string; 8}; 9 10const { data, send } = useJsonStream<{ users: User[] }>("users");11 12const loadUsers = () => {13 send({14 query: "taylor",15 });16};17</script>18 19<template>20 <div>21 <ul>22 <li v-for="user in data?.users" :key="user.id">23 {{ user.id }}: {{ user.name }}24 </li>25 </ul>26 <button @click="loadUsers">Load Users</button>27 </div>28</template>
<script>import { useJsonStream } from "@laravel/stream-svelte";const stream = useJsonStream("users");const loadUsers = () => { stream.send({ query: "taylor", });};</script><div> <ul> {#if $stream.data?.users} {#each $stream.data.users as user (user.id)} <li>{user.id}: {user.name}</li> {/each} {/if} </ul> <button onclick={loadUsers}>Load Users</button></div>
事件流 (SSE)
eventStream 方法可用於返回使用 text/event-stream 內容型別的伺服器推送事件(SSE)流式響應。eventStream 方法接受一個閉包,該閉包應在響應可用時將其 yield 給流。
1Route::get('/chat', function () {2 return response()->eventStream(function () {3 $stream = OpenAI::client()->chat()->createStreamed(...);4 5 foreach ($stream as $response) {6 yield $response->choices[0];7 }8 });9});
如果你想自定義事件名稱,可以 yield 一個 StreamedEvent 類的例項。
1use Illuminate\Http\StreamedEvent;2 3yield new StreamedEvent(4 event: 'update',5 data: $response->choices[0],6);
消費事件流
事件流可以使用 Laravel 的 stream npm 包來消費,該包為與 Laravel 事件流互動提供了便捷的 API。要開始使用,請安裝 @laravel/stream-react、@laravel/stream-vue 或 @laravel/stream-svelte 包。
1npm install @laravel/stream-react
1npm install @laravel/stream-vue
1npm install @laravel/stream-svelte
然後,可以使用 useEventStream 來消費事件流。在提供流 URL 後,該 Hook 會隨著 Laravel 應用程式返回訊息,自動更新 message 為拼接後的響應。
1import { useEventStream } from "@laravel/stream-react";2 3function App() {4 const { message } = useEventStream("/chat");5 6 return <div>{message}</div>;7}
1<script setup lang="ts">2import { useEventStream } from "@laravel/stream-vue";3 4const { message } = useEventStream("/chat");5</script>6 7<template>8 <div>{{ message }}</div>9</template>
1<script>2import { useEventStream } from "@laravel/stream-svelte";3 4const eventStream = useEventStream("/chat");5</script>6 7<div>{$eventStream.message}</div>
傳遞給 useEventStream 的第二個引數是一個選項物件,你可以用它來自定義流消費行為。該物件的預設值如下所示:
1import { useEventStream } from "@laravel/stream-react"; 2 3function App() { 4 const { message } = useEventStream("/stream", { 5 eventName: "update", 6 onMessage: (message) => { 7 // 8 }, 9 onError: (error) => {10 //11 },12 onComplete: () => {13 //14 },15 endSignal: "</stream>",16 glue: " ",17 });18 19 return <div>{message}</div>;20}
1<script setup lang="ts"> 2import { useEventStream } from "@laravel/stream-vue"; 3 4const { message } = useEventStream("/chat", { 5 eventName: "update", 6 onMessage: (message) => { 7 // ... 8 }, 9 onError: (error) => {10 // ...11 },12 onComplete: () => {13 // ...14 },15 endSignal: "</stream>",16 glue: " ",17});18</script>
1<script> 2import { useEventStream } from "@laravel/stream-svelte"; 3 4const eventStream = useEventStream("/chat", { 5 eventName: "update", 6 onMessage: (event) => { 7 // 8 }, 9 onError: (error) => {10 //11 },12 onComplete: () => {13 //14 },15 endSignal: "</stream>",16 glue: " ",17 replace: false,18});19</script>
事件流也可以透過應用程式前端的 EventSource 物件手動消費。當流完成時,eventStream 方法會自動向事件流傳送一個 </stream> 更新。
1const source = new EventSource('/chat'); 2 3source.addEventListener('update', (event) => { 4 if (event.data === '</stream>') { 5 source.close(); 6 7 return; 8 } 9 10 console.log(event.data);11});
要自定義傳送到事件流的最終事件,你可以向 eventStream 方法的 endStreamWith 引數提供一個 StreamedEvent 例項。
1return response()->eventStream(function () {2 // ...3}, endStreamWith: new StreamedEvent(event: 'update', data: '</stream>'));
流式下載
有時你可能希望將某個操作的字串響應轉換為可下載的響應,而無需將操作內容寫入磁碟。在這種情況下,你可以使用 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');
響應宏
如果你想定義一個可以在多個路由和控制器中複用的自定義響應,可以使用 Response 門面上的 macro 方法。通常,你應該在應用程式某個 服務提供者(例如 App\Providers\AppServiceProvider)的 boot 方法中呼叫此方法。
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(): void14 {15 Response::macro('caps', function (string $value) {16 return Response::make(strtoupper($value));17 });18 }19}
macro 函式接受一個名稱作為第一個引數,閉包作為第二個引數。當從 ResponseFactory 實現或 response 輔助函式呼叫該宏名稱時,宏的閉包將被執行。
1return response()->caps('foo');