Laravel 13 API 开发最佳实践

API 是现代应用程序的核心组件。本文将介绍 Laravel 13 API 开发的最佳实践,帮助你构建高质量的 RESTful API。

路由设计

RESTful 路由

1
2
3
4
5
6
7
8
9
10
11
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\UserController;
use App\Http\Controllers\Api\PostController;

Route::prefix('api/v1')->group(function () {
Route::apiResource('users', UserController::class);
Route::apiResource('posts', PostController::class);

Route::get('users/{user}/posts', [UserController::class, 'posts']);
Route::post('posts/{post}/comments', [PostController::class, 'addComment']);
});

路由版本控制

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

Route::prefix('api/v2')->group(function () {
Route::apiResource('users', V2\UserController::class);
});

控制器设计

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

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Http\Resources\UserResource;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;

class UserController extends Controller
{
public function index()
{
$users = User::paginate(15);
return UserResource::collection($users);
}

public function store(StoreUserRequest $request)
{
$user = User::create($request->validated());
return new UserResource($user);
}

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

public function update(UpdateUserRequest $request, User $user)
{
$user->update($request->validated());
return new UserResource($user);
}

public function destroy(User $user)
{
$user->delete();
return response()->json(null, 204);
}
}

资源转换

API 资源

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

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'role' => $this->role,
'created_at' => $this->created_at->toIso8601String(),
'updated_at' => $this->updated_at->toIso8601String(),
];
}
}

资源集合

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

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
'meta' => [
'total' => $this->total(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
'last_page' => $this->lastPage(),
],
'links' => [
'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
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->when($request->user()->isAdmin(), $this->email),
'posts' => PostResource::collection($this->whenLoaded('posts')),
'posts_count' => $this->whenCounted('posts'),
'secret' => $this->when($request->user()->isAdmin(), function () {
return $this->secret;
}),
];
}

表单请求验证

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

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;

class StoreUserRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}

public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
];
}

protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(
response()->json([
'message' => 'Validation failed',
'errors' => $validator->errors(),
], 422)
);
}
}

认证授权

Sanctum 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
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens;
}

// 登录
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);

if (!Auth::attempt($credentials)) {
return response()->json([
'message' => 'Invalid credentials',
], 401);
}

$token = $request->user()->createToken('api-token')->plainTextToken;

return response()->json([
'token' => $token,
'user' => new UserResource($request->user()),
]);
}

// 登出
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json(null, 204);
}

路由保护

1
2
3
4
5
6
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('users', UserController::class);
Route::get('/user', function (Request $request) {
return new UserResource($request->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
30
31
32
// app/Exceptions/Handler.php
public function render($request, Throwable $exception)
{
if ($request->expectsJson()) {
if ($exception instanceof ModelNotFoundException) {
return response()->json([
'message' => 'Resource not found',
], 404);
}

if ($exception instanceof AuthenticationException) {
return response()->json([
'message' => 'Unauthenticated',
], 401);
}

if ($exception instanceof AuthorizationException) {
return response()->json([
'message' => 'Unauthorized',
], 403);
}

if ($exception instanceof ValidationException) {
return response()->json([
'message' => 'Validation failed',
'errors' => $exception->errors(),
], 422);
}
}

return parent::render($request, $exception);
}

分页

分页响应

1
2
3
4
5
6
7
8
9
10
11
12
13
public function index()
{
$users = User::paginate(15);
return UserResource::collection($users);
}

// 自定义分页
$users = User::paginate(
$perPage = request('per_page', 15),
$columns = ['*'],
$pageName = 'page',
$page = request('page', 1)
);

速率限制

API 速率限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// AppServiceProvider.php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}

// routes/api.php
Route::middleware('throttle:api')->group(function () {
Route::apiResource('users', UserController::class);
});

API 文档

注释文档

1
2
3
4
5
6
7
8
9
10
11
/**
* @OA\Get(
* path="/api/users",
* summary="Get list of users",
* @OA\Response(response="200", description="List of users")
* )
*/
public function index()
{
return UserResource::collection(User::paginate());
}

缓存

API 缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public function index()
{
$users = Cache::remember('users.list', 3600, function () {
return User::paginate(15);
});

return UserResource::collection($users);
}

public function show(User $user)
{
$user = Cache::remember("users.{$user->id}", 3600, function () use ($user) {
return $user->load('posts');
});

return new UserResource($user);
}

最佳实践总结

1. 使用资源转换

1
2
3
4
5
// 好的做法
return new UserResource($user);

// 不好的做法
return response()->json($user->toArray());

2. 使用表单请求验证

1
2
3
4
5
6
7
8
9
10
11
// 好的做法
public function store(StoreUserRequest $request)
{
$user = User::create($request->validated());
}

// 不好的做法
public function store(Request $request)
{
$validated = $request->validate([...]);
}

3. 使用 HTTP 状态码

1
2
3
4
5
6
7
8
// 创建成功
return new UserResource($user)->response()->setStatusCode(201);

// 无内容
return response()->json(null, 204);

// 验证失败
return response()->json(['errors' => $errors], 422);

4. 使用路由模型绑定

1
2
3
4
5
6
7
8
9
10
11
12
// 好的做法
public function show(User $user)
{
return new UserResource($user);
}

// 不好的做法
public function show($id)
{
$user = User::findOrFail($id);
return new UserResource($user);
}

总结

Laravel 13 提供了完善的 API 开发支持。通过合理使用资源转换、表单请求验证、认证授权和错误处理,可以构建出高质量的 RESTful API。记住使用资源转换来控制响应格式、使用表单请求来验证输入、使用 Sanctum 来保护 API、并遵循 RESTful 设计原则。API 是现代应用的核心,良好的 API 设计可以提升开发效率和用户体验。