Laravel 13 响应处理详解

响应是 Web 应用与客户端交互的核心。本文将深入探讨 Laravel 13 中各种响应类型和高级用法。

基础响应

字符串响应

1
2
3
4
5
6
7
8
Route::get('/', function () {
return 'Hello World';
});

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

数组与集合响应

1
2
3
4
5
6
7
Route::get('/users', function () {
return [1, 2, 3];
});

Route::get('/users', function () {
return User::all();
});

JSON 响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Illuminate\Http\JsonResponse;

Route::get('/user', function () {
return response()->json([
'name' => 'John',
'email' => 'john@example.com',
]);
});

Route::get('/user', function () {
return response()->json(['name' => 'John'])
->withCallback(request()->input('callback'));
});

Route::get('/users', function () {
return User::all()->toJson();
});

响应对象

创建响应

1
2
3
4
5
6
7
8
9
10
11
12
13
use Illuminate\Http\Response;

$response = new Response('Content', 200);

$response = response('Content', 200)
->header('Content-Type', 'text/html')
->header('X-Custom-Header', 'value');

$response = response('Content')
->withHeaders([
'Content-Type' => 'text/html',
'X-Custom-Header' => 'value',
]);
1
2
3
4
5
6
7
8
9
$response = response('Hello')
->cookie('name', 'value', $minutes)
->cookie('name', 'value', $minutes, $path, $domain, $secure, $httpOnly);

$response = response('Hello')
->cookie(cookie('name', 'value', $minutes));

$response = response('Hello')
->withoutCookie('name');

视图响应

返回视图

1
2
3
4
5
6
7
8
9
10
11
12
13
Route::get('/', function () {
return view('home');
});

Route::get('/', function () {
return view('home', ['name' => 'John']);
});

Route::get('/', function () {
return response()
->view('home', ['name' => 'John'])
->header('X-Custom', 'value');
});

视图响应状态码

1
2
return response()->view('errors.404', [], 404);
return response()->view('errors.500', [], 500);

JSON 响应高级用法

JSONP 响应

1
2
3
return response()
->json(['name' => 'John'])
->withCallback(request()->input('callback'));

分页 JSON 响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Route::get('/users', function () {
$users = User::paginate(15);

return response()->json([
'success' => true,
'data' => $users->items(),
'meta' => [
'current_page' => $users->currentPage(),
'last_page' => $users->lastPage(),
'per_page' => $users->perPage(),
'total' => $users->total(),
],
'links' => [
'first' => $users->url(1),
'last' => $users->url($users->lastPage()),
'prev' => $users->previousPageUrl(),
'next' => $users->nextPageUrl(),
],
]);
});

条件 JSON 响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Route::get('/user/{id}', function ($id) {
$user = User::find($id);

if (!$user) {
return response()->json([
'success' => false,
'message' => '用户不存在',
], 404);
}

return response()->json([
'success' => true,
'data' => $user,
]);
});

文件响应

文件下载

1
2
3
4
5
6
7
8
9
10
11
Route::get('/download', function () {
return response()->download($pathToFile);
});

Route::get('/download', function () {
return response()->download($pathToFile, $name, $headers);
});

Route::get('/download', function () {
return response()->download($pathToFile)->deleteFileAfterSend();
});

流式下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Route::get('/export', function () {
return response()->streamDownload(function () {
echo 'CSV内容...';
}, 'export.csv');
});

Route::get('/export', function () {
$content = 'name,email' . PHP_EOL;
$content .= 'John,john@example.com' . PHP_EOL;

return response()->streamDownload(function () use ($content) {
echo $content;
}, 'users.csv', [
'Content-Type' => 'text/csv',
]);
});

文件显示

1
2
3
4
5
6
7
Route::get('/file', function () {
return response()->file($pathToFile);
});

Route::get('/file', function () {
return response()->file($pathToFile, $headers);
});

流式响应

基础流式响应

1
2
3
4
5
6
7
8
9
10
11
12
13
Route::get('/stream', function () {
return response()->stream(function () {
for ($i = 0; $i < 10; $i++) {
echo "数据行 {$i}\n";
flush();
sleep(1);
}
}, 200, [
'Content-Type' => 'text/plain',
'Cache-Control' => 'no-cache',
'X-Accel-Buffering' => 'no',
]);
});

SSE 流式响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Route::get('/events', function () {
return response()->stream(function () {
while (true) {
$data = json_encode([
'time' => now()->toIso8601String(),
'message' => 'Hello',
]);

echo "data: {$data}\n\n";

if (ob_get_level() > 0) {
ob_flush();
}

flush();

if (connection_aborted()) {
break;
}

sleep(1);
}
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
'X-Accel-Buffering' => 'no',
]);
});

大文件流式导出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Route::get('/export-large', function () {
return response()->stream(function () {
$handle = fopen('php://output', 'w');

fputcsv($handle, ['ID', 'Name', 'Email']);

User::chunk(1000, function ($users) use ($handle) {
foreach ($users as $user) {
fputcsv($handle, [$user->id, $user->name, $user->email]);
}
});

fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="users.csv"',
]);
});

重定向响应

基础重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Route::get('/redirect', function () {
return redirect('/home');
});

Route::get('/redirect', function () {
return redirect()->route('home');
});

Route::get('/redirect', function () {
return redirect()->action([HomeController::class, 'index']);
});

Route::get('/redirect', function () {
return redirect()->back();
});

Route::get('/redirect', function () {
return redirect()->back()->withInput();
});

带参数重定向

1
2
3
4
5
6
7
Route::get('/redirect', function () {
return redirect()->route('user.profile', ['id' => 1]);
});

Route::get('/redirect', function () {
return redirect()->away('https://example.com');
});

闪存数据重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
Route::post('/user', function () {
return redirect('home')->with('status', '用户创建成功');
});

Route::post('/user', function () {
return redirect('home')->withErrors(['name' => '名称不能为空']);
});

Route::post('/user', function () {
return redirect('home')
->withInput()
->withErrors(['name' => '名称不能为空']);
});

API 资源响应

资源类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at->toISOString(),
'links' => [
'self' => route('users.show', $this->id),
],
];
}

public function with($request): array
{
return [
'meta' => [
'version' => '1.0',
],
];
}

public function withResponse($request, $response): void
{
$response->header('X-Resource', 'User');
}
}

资源集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
public function toArray($request): array
{
return [
'data' => $this->collection,
'meta' => [
'total' => $this->total(),
'count' => $this->count(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
'total_pages' => $this->lastPage(),
],
'links' => [
'self' => $this->url($this->currentPage()),
'first' => $this->url(1),
'last' => $this->url($this->lastPage()),
'prev' => $this->previousPageUrl(),
'next' => $this->nextPageUrl(),
],
];
}
}

使用资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use App\Http\Resources\UserResource;
use App\Http\Resources\UserCollection;

Route::get('/user/{id}', function ($id) {
return new UserResource(User::findOrFail($id));
});

Route::get('/users', function () {
return new UserCollection(User::paginate());
});

Route::get('/users', function () {
return UserResource::collection(User::all());
});

响应宏

定义响应宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Response;

class ResponseMacroServiceProvider extends ServiceProvider
{
public function boot(): void
{
Response::macro('success', function ($data = null, string $message = '', int $code = 200) {
return Response::json([
'success' => true,
'data' => $data,
'message' => $message,
], $code);
});

Response::macro('error', function (string $message, int $code = 400, $errors = null) {
return Response::json([
'success' => false,
'message' => $message,
'errors' => $errors,
], $code);
});

Response::macro('paginated', function ($paginator, string $resourceClass) {
return Response::json([
'success' => true,
'data' => $resourceClass::collection($paginator->items()),
'meta' => [
'current_page' => $paginator->currentPage(),
'last_page' => $paginator->lastPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
],
]);
});

Response::macro('csv', function ($data, string $filename = 'export.csv') {
$handle = fopen('php://temp', 'r+');

foreach ($data as $row) {
fputcsv($handle, $row);
}

rewind($handle);
$content = stream_get_contents($handle);
fclose($handle);

return Response::make($content, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
});
}
}

使用响应宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Route::get('/success', function () {
return response()->success(['name' => 'John'], '操作成功');
});

Route::get('/error', function () {
return response()->error('操作失败', 400, ['name' => '名称不能为空']);
});

Route::get('/users', function () {
return response()->paginated(User::paginate(), UserResource::class);
});

Route::get('/export', function () {
$data = [
['ID', 'Name', 'Email'],
[1, 'John', 'john@example.com'],
[2, 'Jane', 'jane@example.com'],
];

return response()->csv($data, 'users.csv');
});

响应中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class AddSecurityHeaders
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);

$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('X-XSS-Protection', '1; mode=block');

if ($request->isSecure()) {
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}

return $response;
}
}

总结

Laravel 13 的响应处理提供了:

  • 多种响应类型支持
  • JSON 和 JSONP 响应
  • 文件下载和流式响应
  • SSE 服务器推送事件
  • 灵活的重定向
  • API 资源转换
  • 可扩展的响应宏

掌握响应处理技巧可以构建更加灵活和高效的 API 接口。