告別胖控制器: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);
}
// ... 接著又是檢查使用者權限 ...
// ... 然後才是商業邏輯 ...
}
這段程式碼雖然能跑,但它犯了兩個架構上的錯誤:
- 職責不清:Controller 的工作應該是「接收請求」並「指揮調度」,而不是當保全人員在門口檢查身分證。
- 難以維護:當同一個驗證邏輯(例如更新使用者資料)需要在多個地方使用時,你就得複製貼上,這就是「義大利麵程式碼」的開始。
第一道防線: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 系統?這裡有幾篇我精選的文章:
- API 金鑰之亂終結者:Laravel + JWT 深度實戰,打造無狀態認證的現代化後端!
- 你的 Controller 還在身兼多職?導入 Service Layer,打造可維護、高彈性的 Laravel Admin 後台架構!
- API 不是能動就好!資深工程師的 REST API 設計宣言:打造溝通無礙、可擴展的數位橋樑
你的 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 類別,也方便在其他地方重複使用。






