Laravel 13 RESTful API 设计最佳实践

摘要

本文将介绍使用 Laravel 13 设计 RESTful API 的最佳实践,包括:

  • RESTful 设计原则
  • 资源路由设计
  • 响应格式规范
  • 错误处理
  • 实战案例

本文适合希望构建高质量 API 的 Laravel 开发者。

1. RESTful 设计原则

1.1 资源命名

1
2
3
4
5
GET    /api/users           # 获取用户列表
POST /api/users # 创建用户
GET /api/users/{id} # 获取单个用户
PUT /api/users/{id} # 更新用户
DELETE /api/users/{id} # 删除用户

1.2 使用复数形式

1
2
3
4
5
6
7
8
9
# 推荐
/api/users
/api/posts
/api/comments

# 不推荐
/api/user
/api/post
/api/comment

1.3 嵌套资源

1
2
3
GET    /api/users/{userId}/posts
POST /api/users/{userId}/posts
GET /api/posts/{postId}/comments

2. 控制器设计

2.1 资源控制器

1
php artisan make:controller Api/UserController --api

2.2 控制器实现

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

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
public function index()
{
$users = User::paginate();

return UserResource::collection($users);
}

public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8',
]);

$user = User::create($validated);

return new UserResource($user);
}

public function show(User $user)
{
return new UserResource($user);
}

public function update(Request $request, User $user)
{
$validated = $request->validate([
'name' => 'sometimes|string|max:255',
'email' => 'sometimes|email|unique:users,email,' . $user->id,
]);

$user->update($validated);

return new UserResource($user);
}

public function destroy(User $user)
{
$user->delete();

return response()->noContent();
}
}

3. 资源响应

3.1 API 资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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->toIso8601String(),
'updated_at' => $this->updated_at->toIso8601String(),
];
}
}

3.2 资源集合

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(),
],
];
}
}

4. 响应格式

4.1 成功响应

1
2
3
4
5
6
7
8
9
10
11
12
13
// 单个资源
return new UserResource($user);

// 资源集合
return UserResource::collection($users);

// 创建成功
return (new UserResource($user))
->response()
->setStatusCode(201);

// 无内容
return response()->noContent();

4.2 错误响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 验证错误
return response()->json([
'message' => 'Validation failed',
'errors' => [
'email' => ['The email field is required.'],
],
], 422);

// 未找到
return response()->json([
'message' => 'Resource not found',
], 404);

// 未授权
return response()->json([
'message' => 'Unauthorized',
], 401);

// 禁止访问
return response()->json([
'message' => 'Forbidden',
], 403);

5. 路由设计

5.1 API 路由

1
2
3
4
5
6
7
8
9
10
11
// routes/api.php
use App\Http\Controllers\Api\UserController;

Route::apiResource('users', UserController::class);

// 嵌套资源
Route::apiResource('users.posts', PostController::class)
->shallow();

// 自定义操作
Route::post('users/{user}/avatar', [UserController::class, 'updateAvatar']);

5.2 版本控制

1
2
3
4
5
6
7
Route::prefix('v1')->group(function () {
Route::apiResource('users', UserController::class);
});

Route::prefix('v2')->group(function () {
Route::apiResource('users', UserControllerV2::class);
});

6. 分页

6.1 基本分页

1
2
3
4
5
6
public function index()
{
$users = User::paginate(15);

return UserResource::collection($users);
}

6.2 简单分页

1
$users = User::simplePaginate(15);

6.3 游标分页

1
$users = User::cursorPaginate(15);

7. 过滤与排序

7.1 过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function index(Request $request)
{
$query = User::query();

if ($request->has('status')) {
$query->where('status', $request->status);
}

if ($request->has('search')) {
$query->where('name', 'like', "%{$request->search}%");
}

return UserResource::collection($query->paginate());
}

7.2 排序

1
2
3
4
5
6
7
8
9
public function index(Request $request)
{
$sort = $request->get('sort', 'created_at');
$order = $request->get('order', 'desc');

$users = User::orderBy($sort, $order)->paginate();

return UserResource::collection($users);
}

8. 最佳实践

8.1 使用表单请求

1
2
3
4
5
6
public function store(StoreUserRequest $request)
{
$user = User::create($request->validated());

return new UserResource($user);
}

8.2 使用资源类

1
2
3
4
5
// 推荐:使用资源类
return new UserResource($user);

// 不推荐:直接返回模型
return $user;

9. 总结

RESTful API 设计的最佳实践:

  1. 资源命名:使用复数形式
  2. HTTP 方法:正确使用 HTTP 方法
  3. 响应格式:统一的响应格式
  4. 错误处理:清晰的错误信息

参考资料