Laravel 11 防線告急?從 Form Request 到 Middleware 的進階防禦工事,拒絕髒資料與越權存取

2026/01/19 | API 串接與自動化, Laravel技術分享, 網站安全與防護

告別胖控制器:Laravel 雙重防線的架構思維

您的 Laravel Controller 是不是腫得像剛拔完智齒的臉頰?本文深入探討如何透過兩道關鍵防線,徹底終結「胖控制器」的惡夢!資深工程師 Eric 揭示 Form Request 如何運用 prepareForValidation 清洗髒資料,並透過 Custom Rules 封裝複雜驗證。同時,結合 Laravel 11 革命性的 Middleware 配置,建立堅實的權限與狀態檢查機制。這種優雅的分層防禦架構,能確保您的程式碼職責分明,大幅提升系統的強韌度與安全性。別讓混亂的程式碼成為資安漏洞的溫床!立即學習進階戰術,打造您的固若金湯 API!

需要專業協助?

聯絡浪花專案團隊 →

嗨,我是 Eric,浪花科技的資深工程師。今天我要放下手邊那杯已經冷掉的咖啡,跟大家好好聊聊 Laravel 開發中,最基礎卻也最容易被「隨便做做」的兩個環節:驗證 (Validation)中介層 (Middleware)

在 Code Review 的時候,我最常看到的驚悚畫面,不是變數命名用 $a$b,而是看到工程師把 50 行的資料驗證邏輯全部塞進 Controller 裡,然後再混雜著權限判斷。這不僅讓 Controller 腫得像剛拔完智齒的臉頰,更是資安漏洞的溫床。

隨著 Laravel 11 的發布,專案結構變得更加精簡(Slim Skeleton),Middleware 的註冊方式也有了翻天覆地的變化。這篇文章不講那些官方文件就有的 CRUD,我們要講的是架構思維:如何優雅地拒絕髒資料,並在請求到達核心邏輯前,築起一道道銅牆鐵壁。

為什麼你的 Controller 總是又臭又長?拒絕「胖控制器」

很多新手(甚至中階)開發者,習慣在 Controller 的 method 開頭寫下這樣的程式碼:

public function store(Request $request) {
    $validator = Validator::make($request->all(), [
        'email' => 'required|email',
        'age' => 'required|integer|min:18',
    ]);

    if ($validator->fails()) {
        return response()->json($validator->errors(), 422);
    }

    // ... 接著又是檢查使用者權限 ...
    // ... 然後才是商業邏輯 ...
}

這段程式碼雖然能跑,但它犯了兩個架構上的錯誤:

  1. 職責不清:Controller 的工作應該是「接收請求」並「指揮調度」,而不是當保全人員在門口檢查身分證。
  2. 難以維護:當同一個驗證邏輯(例如更新使用者資料)需要在多個地方使用時,你就得複製貼上,這就是「義大利麵程式碼」的開始。

第一道防線:Form Request 的進階戰術

Laravel 提供的 Form Request 是將驗證邏輯抽離的最佳解方。但除了基本的 rules() 之外,資深工程師更懂得利用它的 生命週期 hook

1. 善用 prepareForValidation 清洗資料

很多時候,前端傳來的資料格式並不完美。例如,使用者輸入的手機號碼可能帶有空白或連字號,或者全形字元。在驗證之前,我們應該先將資料「標準化」。

與其在驗證後才修整資料,不如在 prepareForValidation 階段就搞定它:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
    protected function prepareForValidation()
    {
        $this->merge([
            // 自動將 email 轉小寫
            'email' => strtolower($this->input('email')),
            // 移除手機號碼中的非數字字元
            'phone' => preg_replace('/[^0-9]/', '', $this->input('phone')),
            // 預設給予一個 slug
            'slug' => $this->input('slug') ?: str()->slug($this->input('name')),
        ]);
    }

    public function rules(): array
    {
        return [
            'email' => 'required|email|unique:users,email',
            'phone' => 'required|string|size:10',
            'slug'  => 'required|string|unique:posts,slug',
        ];
    }
}

這樣做的好處是,你的驗證規則可以直接針對「乾淨」的資料進行檢查,且 Controller 拿到的也會是處理過的完美數據。

2. 自訂驗證規則 (Custom Rules)

不要在 Controller 寫複雜的 if-else 來判斷邏輯。例如,「一個使用者在同一天內只能新增 3 篇文章」,這也是驗證的一環。

php artisan make:rule MaxPostsPerDay

將邏輯封裝在 Rule 物件中,你的 Form Request 依然保持乾淨清爽。這才是物件導向設計 (OOP) 的精髓。

第二道防線:Middleware 與 Laravel 11 的架構革命

如果說 Form Request 是檢查「資料對不對」,那麼 Middleware (中介層) 就是檢查「你有沒有資格做這件事」以及「系統現在讓不讓你做」。

Laravel 11 的重大改變:bootstrap/app.php

在 Laravel 11 之前,我們習慣在 app/Http/Kernel.php 註冊 Middleware。但在 Laravel 11,這個檔案消失了!現在所有的 Middleware 配置都移到了專案根目錄的 bootstrap/app.php

這對於剛升級上來的工程師來說,可能會有點不知所措。讓我們看看如何在 Laravel 11 中設定一個「檢查 API Key」的 Middleware。

實戰:建立與註冊 Middleware

首先,建立 Middleware:

php artisan make:middleware EnsureApiKeyIsValid

撰寫邏輯:

// app/Http/Middleware/EnsureApiKeyIsValid.php
public function handle(Request $request, Closure $next): Response
{
    $apiKey = $request->header('X-API-KEY');

    if ($apiKey !== config('app.api_key')) {
        return response()->json(['message' => 'Invalid API Key'], 401);
    }

    return $next($request);
}

接著,在 Laravel 11 中註冊它。打開 bootstrap/app.php

use App\Http\Middleware\EnsureApiKeyIsValid;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        // 在這裡註冊全域或別名
        
        // 1. 為 Middleware 設定別名 (Alias),方便在 Route 中使用
        $middleware->alias([
            'api.key' => EnsureApiKeyIsValid::class,
        ]);

        // 2. 或者直接加入到 API 群組中,讓所有 API route 自動套用
        // $middleware->api(prepend: [
        //     EnsureApiKeyIsValid::class,
        // ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

現在,你可以在 routes/api.php 中優雅地使用它:

Route::middleware(['api.key'])->group(function () {
    Route::post('/orders', [OrderController::class, 'store']);
});

進階防禦:Middleware 參數化與業務邏輯

Middleware 不僅僅能做權限檢查,它還能處理「前置業務邏輯」。例如,我們有一個多租戶 (Multi-tenant) 系統,需要在進入 Controller 前先確認該租戶是否被停權。

我們可以設計一個 Middleware 接收參數:

// CheckTenantStatus.php
public function handle(Request $request, Closure $next, string $status)
{
    if ($request->user()->tenant->status !== $status) {
        abort(403, 'Your tenant account is not active.');
    }
    return $next($request);
}

在 Route 中使用:

Route::put('/settings', [SettingsController::class, 'update'])
    ->middleware('tenant.status:active');

這樣一來,Controller 完全不需要知道「檢查租戶狀態」這件事,它只需要專注於「更新設定」。這就是 關注點分離 (Separation of Concerns) 的極致表現。

總結:打造固若金湯的 API

回顧一下我們建立的防線:

  • Middleware:在門口擋下沒有權限、API Key 錯誤、或租戶狀態異常的請求。Laravel 11 的 bootstrap/app.php 讓這一層配置更集中。
  • Form Request:確保進入系統的資料格式正確、邏輯合理,並透過 prepareForValidation 預先清洗髒資料。
  • Controller:只需負責呼叫 Service 層處理業務,乾淨利落。

作為資深工程師,我們寫的 Code 不只要能動,更要「耐操」。這種分層防禦的架構,能讓你的 Laravel 專案在面對未來不斷變更的需求時,依然保持優雅與強韌。

如果你發現自己的專案已經充滿了「胖控制器」和四散的驗證邏輯,現在就是重構的最佳時機。別讓技術債拖垮你的產品迭代速度。

延伸閱讀

想更深入了解如何構建高品質的 Laravel 系統?這裡有幾篇我精選的文章:

你的 Laravel 專案正面臨效能瓶頸或資安隱憂嗎?
浪花科技擁有豐富的 Laravel 企業級開發經驗,從架構設計到效能調校,我們能為您打造最堅實的數位堡壘。
立即聯繫我們,預約免費技術諮詢!

常見問題 (FAQ)

Q1: Form Request 和 Middleware 的使用時機該如何區分?

簡單來說,與「特定欄位資料格式」有關的檢查(如 email 格式、密碼長度、欄位間的相依性)請使用 Form Request;與「請求者身分、權限、系統狀態」有關的檢查(如是否登入、是否有 API Key、IP 是否被封鎖)請使用 Middleware。Middleware 是看門的警衛,Form Request 是櫃台的文件審核員。

Q2: Laravel 11 移除了 Kernel.php,舊的 Middleware 該怎麼辦?

如果你是從舊版本升級,Laravel 依然支援舊的結構,不強制遷移。但如果是新專案,所有的 Middleware 設定(包含全域、群組、別名)都必須在 bootstrap/app.php 中的 withMiddleware callback 裡設定。這讓專案結構更輕量化。

Q3: 驗證邏輯很複雜,Form Request 的 rules 方法寫不下怎麼辦?

如果邏輯非常複雜,例如牽涉到外部 API 查詢或複雜的資料庫比對,建議建立自訂的 Rule 物件 (php artisan make:rule)。不要試圖在 Form Request 的陣列裡寫 Closure,那會讓程式碼變得難以閱讀。將複雜邏輯封裝成獨立的 Rule 類別,也方便在其他地方重複使用。

 
立即諮詢,索取免費1年網站保固