Laravel 門神不好當?從自訂驗證到 Middleware,打造滴水不漏的 API 防線
Hey,我是浪花科技的資深工程師 Eric。在寫過大大小小的 Laravel 專案後,我發現很多新手、甚至一些有經驗的開發者,常常會把所有的商業邏輯、資料驗證、權限檢查全部塞在 Controller 裡面。結果就是 Controller 變得又肥又長,幾個月後回來看,連自己都看不懂在寫什麼,簡直是技術債的溫床。老實說,這真的會讓我很頭痛。
其實,Laravel 早就為我們準備了兩大神器來解決這個問題:驗證 (Validation) 和 中介層 (Middleware)。它們就像是你應用程式的兩位門神,一位負責檢查訪客的「證件」是否合規(資料驗證),另一位則負責檢查訪客是否有「權限」進入特定區域(請求過濾)。今天,我就來帶大家深入這兩位門神的內心世界,從基礎觀念到打造專屬的自訂規則與 Middleware,徹底解放你那可憐的 Controller!
Laravel 驗證 (Validation):不只是防君子,更是防駭客
很多開發者會覺得:「我前端不是已經用 JavaScript 做了驗證嗎?為什麼後端還要再做一次?」問得好,這就是我們今天要破解的第一個迷思。
前後端分離的迷思:為什麼後端驗證是你的最後一道防線?
身為一個有點龜毛的工程師,我必須再三強調:千萬、絕對、不要相信任何從客戶端(前端)來的資料!前端驗證的主要目的是為了提供更好的使用者體驗(UX),例如即時提示使用者Email格式錯誤,而不是為了「安全」。
為什麼?因為任何懂一點開發工具的人,都可以輕易地繞過你的前端 JavaScript 驗證,直接對你的 API 發送惡意請求。他們可以送來格式錯誤的資料、意圖注入的 SQL 語法、或是超乎預期的巨量資料。如果你的後端沒有一道堅實的防線,那你的資料庫和應用程式就等於門戶大開,等著被玩壞。
- 前端驗證:為了 UX,是「建議」,可以被繞過。
- 後端驗證:為了安全與資料完整性,是「規則」,不可逾越。
所以,把後端驗證當成你應用程式的最後一道、也是最重要的一道防線,絕對是必要的 paranoid(偏執)。
活用 Laravel 驗證規則,寫出優雅的防禦工事
Laravel 最優雅的驗證方式,我個人首推 Form Request。它能讓你將特定請求的驗證邏輯從 Controller 中抽離出來,變成一個獨立的類別,不僅讓 Controller 乾淨,也讓驗證邏輯可以重複使用。
假設我們要建立一個新增文章的 API,你可以用 Artisan 指令建立一個 Form Request:
php artisan make:request StorePostRequest
然後在產生的 app/Http/Requests/StorePostRequest.php 檔案中定義你的規則:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
// 在這裡可以做權限檢查,例如檢查使用者是否能發布文章
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|string|max:255|unique:posts,title',
'content' => 'required|string',
'category_id' => 'required|integer|exists:categories,id',
];
}
}
看看 `rules` 方法,我們定義了:標題(title)必填、要是字串、最多255字,且在 `posts` 資料表中必須是唯一的。分類 ID (category_id) 則必須存在於 `categories` 資料表中。是不是非常語義化且清晰?
當內建規則不夠用:打造你的專屬驗證規則 (Custom Validation Rule)
有時候,業務邏輯會比 `required` 或 `max:255` 複雜得多。例如,你需要驗證一個欄位是否為有效的台灣身分證字號。這時候,內建規則就不夠用了,我們需要打造自己的驗證規則。
同樣,用 Artisan 指令來幫我們:
php artisan make:rule IsValidTaiwanId
Laravel 會在 app/Rules 資料夾下產生一個 `IsValidTaiwanId.php` 檔案。我們需要實作 `passes` 和 `message` 這兩個方法:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class IsValidTaiwanId implements Rule
{
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
// 這邊只是範例,實際的身分證驗證演算法會更複雜
$value = strtoupper($value);
if (!preg_match('/^[A-Z][12]\d{8}$/', $value)) {
return false;
}
// ... 實作完整的身分證字號檢查邏輯 ...
return true;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return '所提供的身分證字號格式不正確。';
}
}
寫好之後,就可以在 Form Request 中像使用內建規則一樣使用它了:
use App\Rules\IsValidTaiwanId;
// ... in your Form Request
public function rules()
{
return [
'id_card_number' => ['required', new IsValidTaiwanId()],
];
}
看,是不是超優雅?複雜的邏輯被封裝起來,讓你的驗證規則像積木一樣可以隨意組合。
Middleware:請求的洋蔥式篩檢站
如果說 Validation 是檢查「證件內容」,那 Middleware 就是在門口檢查「入場券」的保全。它是一種過濾 HTTP 請求的機制。
Middleware 到底是什麼?把它想像成 API 的保全系統
你可以把 Middleware 想像成一層層的洋蔥。當一個請求進來時,它會先穿過最外層的 Middleware,如果通過,再進到下一層,直到最後抵達核心(你的 Controller)。在這個過程中,任何一層 Middleware 都可以決定要不要把這個請求擋下來,或者在請求上附加一些資訊再往下傳。
Laravel 內建了很多好用的 Middleware,例如 `auth` 用來檢查使用者是否登入,`throttle` 用來限制請求頻率(防止暴力破解)。而當內建的保全不符合你的需求時,就是我們自己動手打造的時候了。
實戰:打造一個檢查 API Key 的自訂 Middleware
假設我們有一個只開放給合作夥伴使用的 API,我們要求他們在請求的 Header 中帶上一個特定的 `X-API-KEY` 來驗證身份。
Step 1: 建立 Middleware
php artisan make:middleware VerifyApiKey
這會在 `app/Http/Middleware` 中建立 `VerifyApiKey.php`。我們來編輯它的 `handle` 方法:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class VerifyApiKey
{
public function handle(Request $request, Closure $next)
{
$apiKey = $request->header('X-API-KEY');
// 從 .env 取得我們預設的正確 API Key
$validApiKey = config('services.partner.api_key');
if (!$apiKey || $apiKey !== $validApiKey) {
// 如果驗證失敗,直接回傳 403 Forbidden
return response()->json(['message' => 'Unauthorized Access.'], 403);
}
// 驗證通過,將請求交給下一個 Middleware 或 Controller
return $next($request);
}
}
Step 2: 註冊 Middleware
光是建立還不夠,要讓 Laravel 認得它。打開 app/Http/Kernel.php,在 $routeMiddleware 陣列中加入一行:
protected $routeMiddleware = [
// ... other middlewares
'api.key' => \App\Http\Middleware\VerifyApiKey::class,
];
我們給了它一個簡短好記的名字 `api.key`。
Step 3: 套用到路由
現在,我們可以在路由檔案(例如 `routes/api.php`)中,把這個 Middleware 套用在需要保護的路由上:
use App\Http\Controllers\PartnerDataController;
Route::get('/partner-data', [PartnerDataController::class, 'index'])
->middleware('api.key');
搞定!現在任何要存取 `/partner-data` 這個端點的請求,都必須先通過 `VerifyApiKey` 這個保全的檢查,否則連 Controller 的門都摸不到。
組合技:Validation + Middleware,讓你的 Controller 瘦成一道閃電
現在我們把兩大神器組合起來。想像一下,我們的合作夥伴要透過 API 新增一筆訂單。這個請求需要:
1. 身份驗證(API Key 要對)。
2. 資料驗證(訂單欄位要符合格式)。
我們的路由會長這樣:
Route::post('/orders', [OrderController::class, 'store'])
->middleware('api.key');
然後我們的 `OrderController` 中的 `store` 方法,會使用前面提到的 Form Request 來處理驗證:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreOrderRequest; // 這是我們的 Form Request
use App\Models\Order;
use Illuminate\Http\Request;
class OrderController extends Controller
{
public function store(StoreOrderRequest $request)
{
// 如果程式能執行到這裡,代表:
// 1. API Key 已經通過 Middleware 驗證
// 2. 請求的資料已經通過 StoreOrderRequest 的驗證
// Controller 只需要專注在核心的商業邏輯
$validatedData = $request->validated();
$order = Order::create($validatedData);
return response()->json($order, 201);
}
}
看看這個 Controller,是不是乾淨到令人感動?它完全不用管驗證的細節,只專注於「建立訂單」這一件事。這就是關注點分離 (Separation of Concerns) 的最佳實踐!
工程師的小囉嗦:那些年我們踩過的坑
最後,分享幾個我在實務上總結出來的經驗:
- Middleware 的順序很重要:在 `Kernel.php` 或路由群組中定義的 Middleware 是有執行順序的。例如,`auth` 應該放在 `throttle` 前面,避免未登入的訪客也佔用請求限制的額度。
- 不要在 Middleware 中執行耗時操作:Middleware 應該要快狠準。如果你需要在每個請求都做複雜的資料庫查詢,考慮一下是不是有更好的架構,例如快取。
- 自訂驗證規則的錯誤訊息要明確:不要只回傳「格式錯誤」,盡量告訴使用者「為什麼」錯,以及「該如何」修正。
- 善用 `abort()` 輔助函數:在 Middleware 或 Form Request 的 `authorize` 方法中,使用 `abort(403, ‘You do not have permission.’)` 可以快速回傳標準的 HTTP 錯誤回應,程式碼更簡潔。
- 記得寫測試!:你的自訂規則和 Middleware 也是程式碼的一部分,為它們撰寫單元測試或功能測試,確保它們在各種情境下都能如預期般運作。
掌握了 Laravel 的驗證與 Middleware,你不僅能寫出更安全、更穩固的程式碼,更能打造出結構清晰、易於維護的應用程式架構。這不只是技巧,更是一種工程師的品味與專業。希望今天的分享對你有幫助!
延伸閱讀
- 金牌給你,API 給我鎖!Laravel + JWT 打造銅牆鐵壁般的 API 認證機制全解析
- 肥 Controller 瘦不下來?Laravel 後台架構終極對決:Repository vs. Action 模式,資深工程師帶你選對屠龍刀!
- 別再讓你的 API 裸奔!資深工程師的 Laravel Webhook 安全實戰:從設計到簽名驗證,打造滴水不漏的自動化橋樑
如果你們的團隊正在尋找專業的 Laravel 技術夥伴,或是對現有的系統架構感到頭痛,想進行一場徹底的健康檢查與優化,歡迎與浪花科技的團隊聊聊。我們樂於分享我們的經驗,協助你的專案走向成功。
常見問題 (FAQ)
Q1: Laravel 驗證 (Validation) 和中介層 (Middleware) 有什麼核心不同?
簡單來說,Validation 專注於「資料」的正確性,確保傳入的資料符合我們預設的格式、類型和業務規則。而 Middleware 專注於「請求」的合法性,它在請求到達 Controller 之前進行過濾,處理如身份驗證、權限檢查、請求頻率限制等橫切關注點 (Cross-Cutting Concerns)。Validation 是在檢查包裹裡的物品,Middleware 是在檢查送包裹的人有沒有通行證。
Q2: 什麼時候我應該建立自訂的驗證規則 (Custom Validation Rule)?
當 Laravel 內建的驗證規則無法滿足你的特定業務需求時,就應該建立自訂規則。例如:驗證台灣手機號碼格式、檢查統一編號是否有效、確認某個自訂的優惠碼是否存在且可用等等。將這些複雜邏輯封裝成一個獨立的 Rule Class,可以讓你的程式碼更乾淨、更易於重複使用和測試。
Q3: 什麼時候我應該建立自訂的 Middleware?
當你需要對「一組」路由或所有請求執行相同的「前置處理」或「後置處理」時,就適合使用自訂 Middleware。常見情境包括:檢查特定的 API 金鑰、根據使用者的角色或訂閱等級來限制存取、記錄所有傳入的 API 請求日誌、或是在系統維護時顯示一個維護中頁面。基本上,任何不屬於單一 Controller 核心業務邏輯,但又需要在請求生命週期中執行的檢查或操作,都很適合放在 Middleware 中。
Q4: 我可以對一個路由使用多個 Middleware 嗎?它們的執行順序是?
當然可以!你可以在路由定義中使用一個陣列來指定多個 Middleware,例如:->middleware(['auth:api', 'api.key', 'check.role:admin'])。它們的執行順序就是你在陣列中定義的順序,由左至右依序執行。請求會像穿越隧道一樣,依序通過每一個 Middleware 的檢查,只要其中一個 Middleware 決定阻擋請求,後續的 Middleware 和目標 Controller 都不會被執行到。






