2025年8月3日 星期日

台灣資深工程師帶你走,用這六個 Laravel API 設計模式打造出「穩」又「巧」的 API

嗨,各位夥伴!身為一名在資訊業打滾多年的老鳥,我看過太多專案從初期的新穎活潑,到後期因為設計不良而變得難以維護、寸步難行。特別是在 API 開發上,一個好的起點至關重要。

Laravel 作為一個優雅且功能強大的框架,其實已經為我們鋪好了許多康莊大道。今天,我想跟大家分享六個我個人在專案中最愛用、也最推薦的 API 設計模式。它們不只是「好用」,更是能讓你的程式碼更穩固、更具彈性、更好維護的關鍵。

1. 資源控制器與隱式路由模型綁定:寫路由,別再寫撈資料的爛攤子

一開始寫 Laravel API,最常見的模式可能是這樣:

PHP
// routes/api.php
Route::get('/posts/{id}', [PostController::class, 'show']);

// PostController.php
public function show($id)
{
    $post = Post::findOrFail($id);
    // ... 後續邏輯
}

這沒什麼問題,但當專案規模擴大,你開始會發現 findOrFailfirstOrFail 這樣的程式碼,幾乎在每個 showupdatedestroy 方法中重複出現。這就是一種浪費。

Laravel 的**隱式路由模型綁定(Implicit Route Model Binding)**就是為了解決這個痛點而生的。

PHP
// routes/api.php
Route::resource('posts', PostController::class); // 只需一行

// PostController.php
public function show(Post $post)
{
    // $post 已經自動被注入,可以直接使用
    return new PostResource($post);
}

為什麼我這麼推崇它?

  • 程式碼更乾淨:控制器從尋找模型的瑣碎工作解脫,只專注於處理業務邏輯。

  • 內建錯誤處理:如果找不到對應的 Post,Laravel 會自動回傳 404 錯誤,無需我們手動處理。

  • 一致性Route::resource 能自動為你產生完整的 RESTful API 路由,確保命名與行為的一致性。

這是一個小小的改變,卻能讓你的控制器程式碼更優雅,也更不易出錯。

2. API 資源:別把你的資料庫赤裸裸地暴露在外面

當你從資料庫撈出一個 Post 模型,它可能包含了 created_atupdated_at、甚至是 deleted_at 等內部欄位。直接將這個模型轉成 JSON 回傳給前端,不僅會暴露過多不必要的資訊,更會導致 API 結構與資料庫結構緊密耦合。

這時候,**API 資源(API Resources)**就是你的救星。

PHP
// PostResource.php
class PostResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'author_name' => $this->author->name,
            'published_on' => $this->published_at->format('Y-m-d'),
        ];
    }
}

這個模式讓你能夠:

  • 資料格式化:將 published_at 這種日期格式化成前端需要的格式。

  • 欄位篩選:只回傳前端需要的欄位,隱藏如 password 或其他敏感資料。

  • 資料關聯:可以直接在資源中處理關聯模型(例如 $this->author->name),讓回傳的 JSON 結構更直觀。

  • 解耦:當你調整資料庫欄位時,只要修改 API 資源,前端接口就不會受到影響。

它像是在你的資料庫和外部世界之間,建立了一層堅固的翻譯官與守門員。

3. 服務層模式:把控制器當成服務生,把業務邏輯交給廚師

許多新手工程師會習慣把所有邏輯,從驗證、資料庫操作到發送通知,都塞進控制器裡。這會導致一個問題:當多個地方需要重複這段邏輯時,你就得複製貼上,甚至在控制器中寫出數百行的「巨型方法」。

我個人強烈建議採用服務層模式(Service Layer Pattern)

PHP
// PostService.php
class PostService
{
    public function createPost(array $data)
    {
        // 這裡可以處理更複雜的業務邏輯,例如:
        // 1. 檢查使用者權限
        // 2. 創建 Post
        // 3. 觸發事件或發送通知
        return Post::create($data);
    }
}

// PostController.php
public function store(StorePostRequest $request, PostService $service)
{
    // 控制器只負責協調工作,將業務邏輯交給服務
    $post = $service->createPost($request->validated());
    return new PostResource($post);
}

這麼做的好處顯而易見:

  • 關注點分離:控制器只處理 HTTP 請求(Request)與回應(Response),服務層專注於處理業務邏輯。

  • 邏輯重用:服務層的程式碼可以在控制器、排程任務(Scheduled Jobs)、命令列工具(Artisan Commands)中重複使用,大幅提高開發效率。

  • 可測試性:服務層的程式碼更易於單元測試,你可以輕鬆地對其進行模擬和測試,而無需考慮 HTTP 請求的上下文。

4. 表單請求:把驗證邏輯從控制器中請出去

在控制器中寫一堆 $request->validate() 是個壞習慣。它會讓你的控制器變得臃腫且難以閱讀。

PHP
public function store(Request $request)
{
    $request->validate([
        'title' => 'required|string|max:255',
        'content' => 'required|string',
    ]);
    // ... 後續邏輯
}

Laravel 提供的表單請求(Form Requests),就是一種更優雅的解決方案。

PHP
// StorePostRequest.php
class StorePostRequest extends FormRequest
{
    public function rules()
    {
        return [
            'title' => 'required|string|max:255',
            'content' => 'required|string',
        ];
    }
}

// PostController.php
public function store(StorePostRequest $request)
{
    // 如果驗證失敗,Laravel 會自動返回 422 錯誤
    // 控制器只需關注通過驗證的資料
    $post = Post::create($request->validated());
    return new PostResource($post);
}

它不僅讓控制器更簡潔,還能:

  • 集中管理規則:所有驗證規則都集中在一個類別中,方便管理。

  • 自動化錯誤回應:Laravel 會自動將驗證錯誤轉換為標準的 JSON 格式,並帶上 422 狀態碼,符合 API 設計規範。

  • 權限驗證:在 FormRequestauthorize() 方法中,你還可以進行權限檢查,確保只有有權限的使用者才能執行此操作。

5. API 版本控制:當你的 API 長大後,如何優雅地進化

當你的 API 已經上線並有許多客戶端在使用時,如果需要做一個會破壞向後相容性的修改,該怎麼辦?直接修改舊的 API 絕對是災難。這時候,版本控制就顯得至關重要。

最簡單且有效的方式,就是使用路由前綴來進行版本控制。

PHP
// routes/api.php

// 舊版 API
Route::prefix('v1')->group(function () {
    Route::resource('posts', PostController::class);
});

// 新版 API
Route::prefix('v2')->group(function () {
    Route::resource('posts', PostV2Controller::class);
});

這種做法的優點是:

  • 不影響舊客戶:舊的客戶端可以繼續使用 /api/v1/posts,不受任何影響。

  • 平滑過渡:你可以給予客戶端足夠的時間,將他們的服務從 v1 遷移到 v2。

  • 程式碼隔離:將不同版本的控制器與邏輯分開,讓程式碼結構更清晰。

6. 速率限制與流量節流:保護你的 API 不被惡意攻擊

一個開放的 API 很容易成為被攻擊的目標,或被惡意程式瘋狂發送請求。如果沒有保護機制,你的伺服器很快就會不堪重負。

Laravel 內建的**速率限制(Rate Limiting)**中介層,可以輕鬆地解決這個問題。

PHP
// routes/api.php
Route::middleware('throttle:60,1')->group(function () {
    Route::resource('posts', PostController::class);
});

// 你也可以為不同的用戶或路由定義不同的限制
Route::middleware('throttle:api')->group(function () {
    // 預設 'api' 節流器定義在 App\Providers\RouteServiceProvider.php
    Route::resource('posts', PostController::class);
});

透過簡單的設定,你就能:

  • 防止伺服器超載:限制單一 IP 或使用者的請求頻率,保護你的伺服器資源。

  • 提升安全性:有效阻擋暴力破解密碼等惡意行為。

  • 公平使用:確保所有使用者都能獲得穩定的服務。

別忘了,你還可以針對已登入使用者,透過動態綁定來設定更寬鬆的限制。這點在 app/Providers/RouteServiceProvider.php 中都有詳細的配置範例。


結語

這些設計模式並非萬靈丹,但它們是經過實戰驗證的最佳實踐。在專案初期就養成使用這些模式的好習慣,能讓你從一個只會寫功能的工程師,蛻變成一個能思考架構、寫出優雅程式碼的專業開發者。

希望這篇文章能幫助你在 Laravel 的 API 開發之路上走得更遠、更穩。如果對其中任何一點有更深入的疑問,隨時可以一起交流!

沒有留言:

張貼留言

熱門文章