Laravel 13 JSON 处理完全指南

JSON 是现代 Web API 的标准数据格式。本文将深入探讨 Laravel 13 中 JSON 处理的各种方法和最佳实践。

JSON 响应

基础 JSON 响应

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

// 简单 JSON 响应
return response()->json(['name' => 'John', 'status' => 'active']);

// 带状态码
return response()->json(['error' => 'Not found'], 404);

// 带自定义头
return response()->json(['data' => $data])
->header('X-Custom-Header', 'value');

// JSONP 响应
return response()->json(['data' => $data])
->withCallback($request->input('callback'));

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
36
37
38
39
40
41
42
43
44
45
46
<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;

class ApiController extends Controller
{
protected function successResponse($data = null, string $message = '', int $code = 200): JsonResponse
{
return response()->json([
'success' => true,
'data' => $data,
'message' => $message,
], $code);
}

protected function errorResponse(string $message, int $code = 400, $errors = null): JsonResponse
{
return response()->json([
'success' => false,
'message' => $message,
'errors' => $errors,
], $code);
}

protected function paginatedResponse($paginator, string $resourceClass): JsonResponse
{
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(),
],
'links' => [
'first' => $paginator->url(1),
'last' => $paginator->url($paginator->lastPage()),
'prev' => $paginator->previousPageUrl(),
'next' => $paginator->nextPageUrl(),
],
]);
}
}

JSON 编码选项

常用选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 美化输出
return response()->json($data)
->setEncodingOptions(JSON_PRETTY_PRINT);

// 不转义 Unicode
return response()->json($data)
->setEncodingOptions(JSON_UNESCAPED_UNICODE);

// 不转义斜杠
return response()->json($data)
->setEncodingOptions(JSON_UNESCAPED_SLASHES);

// 组合选项
return response()->json($data)
->setEncodingOptions(JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

自定义 JSON 编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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(),
];
}

public function jsonOptions(): int
{
return JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE;
}
}

JSON 输入处理

获取 JSON 输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function store(Request $request)
{
// 获取所有输入
$data = $request->all();

// 获取 JSON 输入
$json = $request->json()->all();

// 获取特定字段
$name = $request->input('name');
$email = $request->json('email');

// 嵌套数据
$street = $request->input('address.street');
$phones = $request->input('phones.*.number');
}

JSON 验证

1
2
3
4
5
6
7
8
9
10
public function store(Request $request)
{
$request->validate([
'data' => ['required', 'json'],
'data.name' => ['required', 'string'],
'data.email' => ['required', 'email'],
]);

$data = json_decode($request->data, true);
}

JSON 资源

资源类

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
<?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,
'avatar' => $this->avatar_url,
'profile' => new ProfileResource($this->whenLoaded('profile')),
'posts_count' => $this->whenCounted('posts'),
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
'links' => [
'self' => route('api.users.show', $this->id),
'posts' => route('api.users.posts', $this->id),
],
];
}

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

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

资源集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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(),
],
];
}
}

JSON 处理服务

完整 JSON 服务

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php

namespace App\Services;

class JsonService
{
protected int $encodeOptions = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
protected int $decodeDepth = 512;
protected int $decodeFlags = 0;

public function encode($data, bool $pretty = false): string
{
$options = $this->encodeOptions;
if ($pretty) {
$options |= JSON_PRETTY_PRINT;
}

$json = json_encode($data, $options);

if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException('JSON encode error: ' . json_last_error_msg());
}

return $json;
}

public function decode(string $json, bool $assoc = true): mixed
{
$data = json_decode($json, $assoc, $this->decodeDepth, $this->decodeFlags);

if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException('JSON decode error: ' . json_last_error_msg());
}

return $data;
}

public function validate(string $json): bool
{
json_decode($json);
return json_last_error() === JSON_ERROR_NONE;
}

public function prettyPrint(string $json): string
{
$data = $this->decode($json);
return $this->encode($data, true);
}

public function merge(string $json1, string $json2): string
{
$data1 = $this->decode($json1, true);
$data2 = $this->decode($json2, true);

return $this->encode(array_merge($data1, $data2));
}

public function extract(string $json, string $path): mixed
{
$data = $this->decode($json, true);
$keys = explode('.', $path);

foreach ($keys as $key) {
if (!is_array($data) || !array_key_exists($key, $data)) {
return null;
}
$data = $data[$key];
}

return $data;
}

public function transform(string $json, callable $callback): string
{
$data = $this->decode($json, true);
$result = $callback($data);
return $this->encode($result);
}
}

JSON Schema 验证

Schema 验证器

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<?php

namespace App\Services;

class JsonSchemaValidator
{
public function validate($data, array $schema): array
{
$errors = [];
$this->validateSchema($data, $schema, '', $errors);

return [
'valid' => empty($errors),
'errors' => $errors,
];
}

protected function validateSchema($data, array $schema, string $path, array &$errors): void
{
$type = $schema['type'] ?? null;

if ($type === 'object') {
$this->validateObject($data, $schema, $path, $errors);
} elseif ($type === 'array') {
$this->validateArray($data, $schema, $path, $errors);
} else {
$this->validateScalar($data, $schema, $path, $errors);
}
}

protected function validateObject($data, array $schema, string $path, array &$errors): void
{
if (!is_array($data) || !$this->isAssociative($data)) {
$errors[] = ['path' => $path, 'message' => 'Expected object'];
return;
}

$required = $schema['required'] ?? [];
$properties = $schema['properties'] ?? [];

foreach ($required as $field) {
if (!array_key_exists($field, $data)) {
$errors[] = [
'path' => $path,
'message' => "Missing required field: {$field}",
];
}
}

foreach ($properties as $field => $fieldSchema) {
if (array_key_exists($field, $data)) {
$fieldPath = $path ? "{$path}.{$field}" : $field;
$this->validateSchema($data[$field], $fieldSchema, $fieldPath, $errors);
}
}
}

protected function validateArray($data, array $schema, string $path, array &$errors): void
{
if (!is_array($data)) {
$errors[] = ['path' => $path, 'message' => 'Expected array'];
return;
}

$items = $schema['items'] ?? [];

foreach ($data as $index => $item) {
$itemPath = $path ? "{$path}[{$index}]" : "[{$index}]";
$this->validateSchema($item, $items, $itemPath, $errors);
}
}

protected function validateScalar($data, array $schema, string $path, array &$errors): void
{
$type = $schema['type'] ?? null;

$valid = match ($type) {
'string' => is_string($data),
'number' => is_numeric($data),
'integer' => is_int($data),
'boolean' => is_bool($data),
'null' => is_null($data),
default => true,
};

if (!$valid) {
$errors[] = ['path' => $path, 'message' => "Expected type: {$type}"];
}

if (isset($schema['enum']) && !in_array($data, $schema['enum'])) {
$errors[] = ['path' => $path, 'message' => 'Value not in allowed enum'];
}

if (isset($schema['minLength']) && strlen($data) < $schema['minLength']) {
$errors[] = ['path' => $path, 'message' => "Minimum length: {$schema['minLength']}"];
}

if (isset($schema['maxLength']) && strlen($data) > $schema['maxLength']) {
$errors[] = ['path' => $path, 'message' => "Maximum length: {$schema['maxLength']}"];
}
}

protected function isAssociative(array $array): bool
{
return array_keys($array) !== range(0, count($array) - 1);
}
}

JSON 缓存

JSON 文件缓存

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
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;

class JsonCache
{
public function remember(string $key, int $ttl, callable $callback): mixed
{
return Cache::remember($key, $ttl, function () use ($callback) {
$data = $callback();
return json_encode($data, JSON_UNESCAPED_UNICODE);
});
}

public function get(string $key): ?array
{
$json = Cache::get($key);
return $json ? json_decode($json, true) : null;
}

public function put(string $key, array $data, int $ttl): bool
{
return Cache::put($key, json_encode($data, JSON_UNESCAPED_UNICODE), $ttl);
}
}

总结

Laravel 13 的 JSON 处理提供了:

  • 灵活的 JSON 响应构建
  • 完善的 API 响应格式
  • 强大的资源转换
  • JSON 编码选项控制
  • Schema 验证支持
  • JSON 缓存机制

掌握 JSON 处理技巧是构建现代 API 的基础。