~/blog/laravel-11-validation-middleware-customization-guide.md
Laravel 與後端開發 · 2026 / 01 / 01 · 6 views

擋下髒資料:Laravel 11 Validation 與客製化 Middleware 實戰攻略

Eric — 浪花科技創辦人 / AI 架構師
Eric
浪花科技創辦人 · AI 架構師
擋下髒資料:Laravel 11 Validation 與客製化 Middleware 實戰攻略
目錄 table-of-contents.md

後端最危險的假設,就是相信前端傳來的資料是乾淨的。表單驗證做在前端只是體驗,做在後端才是防線;Laravel 11 的 Validation 搭配客製化 Middleware,能在請求進入商業邏輯之前就把髒資料擋在門外。這篇從驗證規則寫到 Middleware 客製化,把這道防線實際建起來。

你是否曾經接手過這種專案:打開 Controller,迎面而來的是幾十行的 if-else 判斷?

  • 「如果有 email 欄位...」
  • 「如果 password 大於 6 個字...」
  • 「如果這個使用者是管理員...」

這種寫法我稱之為「義大利麵防線」,看起來有在擋,但隨便戳一下就破了,而且維護起來簡直是地獄。在 Laravel 的世界裡,我們有兩把屠龍刀可以優雅地解決這個問題:驗證 (Validation)中介層 (Middleware)

特別是到了 2025 年,Laravel 11 在架構上做了不小的瘦身(移除了許多預設 config 檔案),很多原本寫在 Kernel.php 的設定現在都搬到了 bootstrap/app.php。這篇文章我將會用最新的 Laravel 11 觀念,帶你打造銅牆鐵壁般的 API。

為什麼你的 API 像是在「裸奔」?

很多初階工程師會覺得:「反正前端有做表單驗證了,後端隨便寫寫就好。」

大錯特錯!

前端驗證是用來「提升使用者體驗」的,不是用來「防駭客」的。任何人都可以透過 Postman 或 cURL 直接對你的 API 發送請求,繞過所有的前端驗證。如果你的 Laravel 後端沒有做好把關,惡意資料就會像喪屍一樣湧入你的資料庫。

Middleware vs. Validation:誰負責什麼?

在開始寫 Code 之前,我們要先釐清觀念,這也是很多面試會考的題目:

  • Middleware (中介層): 像是大樓的保全。他不管你包包裡裝什麼,他只看你有沒有識別證 (Token)、是不是被列入黑名單 (IP Ban)、或是現在是不是開放時間 (Rate Limiting)。
  • Validation (驗證): 像是櫃檯的行政人員。保全放你進來後,行政人員會檢查你的申請表 (Request Data) 有沒有填寫正確、Email 格式對不對、商品庫存夠不夠。

第一道防線:客製化 Middleware (Laravel 11 新制)

假設我們有一個需求:只有來自特定合作夥伴 API Key 的請求才能存取我們的特定路由

首先,我們建立一個 Middleware:

php artisan make:middleware CheckPartnerApiKey

接著,打開產生的 app/Http/Middleware/CheckPartnerApiKey.php。我們要檢查 Header 裡是否有正確的 Key。

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckPartnerApiKey
{
    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next): Response
    {
        $validApiKey = config('services.partner.api_key'); // 建議將 Key 放在 .env 並透過 config 讀取
        $requestApiKey = $request->header('X-Partner-Key');

        if ($requestApiKey !== $validApiKey) {
            return response()->json([
                'status' => 'error',
                'message' => 'Unauthorized: Invalid API Key',
            ], 401);
        }

        return $next($request);
    }
}

Laravel 11 的註冊方式變革

在 Laravel 10 以前,我們會去 app/Http/Kernel.php 註冊。但在 Laravel 11Kernel.php 已經消失了!現在我們要在 bootstrap/app.php 進行設定,這是一個更簡潔的現代化做法。

<?php

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

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) {
        // 註冊別名 (Alias),方便在 Route 中使用
        $middleware->alias([
            'partner.auth' => CheckPartnerApiKey::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

現在,你就可以在 routes/api.php 中輕鬆使用了:

Route::middleware(['partner.auth'])->group(function () {
    Route::post('/partner/sync', [PartnerController::class, 'syncData']);
});

第二道防線:Form Request Validation (表單請求驗證)

Middleware 擋掉了閒雜人等,接下來要處理資料正確性。請絕對不要在 Controller 裡面寫 $request->validate([...]),那會讓你的 Controller 變得非常肥大且難以閱讀。請使用 Form Request

假設我們要建立一個「新增產品」的 API。

php artisan make:request StoreProductRequest

打開 app/Http/Requests/StoreProductRequest.php,這裡有兩個核心方法:authorize()rules()

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreProductRequest extends FormRequest
{
    /**
     * 判斷使用者是否有權限執行此請求
     */
    public function authorize(): bool
    {
        // 這裡可以整合 Policy,例如:
        // return $this->user()->can('create', Product::class);
        return true; 
    }

    /**
     * 定義驗證規則
     */
    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'sku' => ['required', 'string', 'unique:products,sku'], // 檢查資料庫唯一性
            'price' => ['required', 'numeric', 'min:0'],
            'categories' => ['required', 'array', 'min:1'],
            'categories.*' => ['exists:categories,id'], // 檢查陣列中的每個 ID 是否存在於資料庫
            'is_active' => ['boolean'],
        ];
    }

    /**
     * 自訂錯誤訊息 (可選,適合多語系或更友善的提示)
     */
    public function messages(): array
    {
        return [
            'sku.unique' => '這組 SKU 已經有人用過了,請換一組吧!',
            'categories.*.exists' => '您選取的分類不存在,請重新整理頁面。',
        ];
    }
}

在 Controller 中使用

這就是最迷人的地方。當你將 Type Hint 指定為你剛剛建立的 StoreProductRequest 時,Laravel 會自動在進入 Controller 方法之前執行驗證。如果驗證失敗,它會自動回傳 JSON 格式的 422 錯誤訊息,Controller 的程式碼一行都不會被執行!

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreProductRequest;
use App\Models\Product;
use Illuminate\Http\JsonResponse;

class ProductController extends Controller
{
    public function store(StoreProductRequest $request): JsonResponse
    {
        // 能進到這裡,代表資料絕對是乾淨、正確的
        // 不需要再寫任何 if-else 檢查欄位
        
        // 直接取用驗證過的資料
        $validated = $request->validated();

        $product = Product::create($validated);

        return response()->json($product, 201);
    }
}

看,你的 Controller 是不是清爽到想哭?這就是「關注點分離 (Separation of Concerns)」的藝術。

進階技巧:自訂驗證規則 (Custom Rules)

有時候,Laravel 內建的規則不夠用。例如,你的電商系統規定:「如果是預購商品 (Pre-order),出貨日期必須在 30 天以後」。這時候就需要自訂規則。

php artisan make:rule PreOrderDateCheck

編寫邏輯:

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Carbon\Carbon;

class PreOrderDateCheck implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // 假設 request 中有另一個欄位叫 type
        if (request()->input('type') === 'pre_order') {
            $date = Carbon::parse($value);
            if ($date->diffInDays(now()) < 30) {
                $fail('預購商品的 :attribute 必須設定在 30 天之後。');
            }
        }
    }
}

然後在 Form Request 中使用:

use App\Rules\PreOrderDateCheck;

// ... inside rules array
'shipping_date' => ['required', 'date', new PreOrderDateCheck],

結論:寫出優雅的 Code,晚上睡得比較好

作為一名資深工程師,我深信「防禦性程式設計 (Defensive Programming)」的核心不在於你寫了多少複雜的邏輯,而在於你如何利用框架提供的工具,把不乾淨的資料擋在門外。

利用 Laravel 11 的 Middleware 來管理存取權限,利用 Form Request 來確保資料完整性,你的應用程式將會變得像一座堅固的城堡。駭客進不來,髒資料進不來,你的 Controller 就能專心地處理商業邏輯,而不是在那邊當清潔工。

推薦閱讀

如果你對 Laravel 的架構安全與資料處理有興趣,強烈建議你延伸閱讀以下幾篇我精選的文章,這能讓你的技術視野更上一層樓:

如果你發現你的 Laravel 專案已經變成一團難以維護的義大利麵,或者你需要企業級的 API 架構規劃,別猶豫,讓我們來幫你重構出藝術品般的程式碼。

需要專業的 Laravel 架構諮詢嗎?

浪花科技擁有豐富的 Laravel 企業級開發經驗,從高流量 API 到複雜的 SaaS 系統,我們都能提供最穩健的解決方案。別讓技術債拖垮你的業務成長!

立即填寫表單聯繫 Eric 與技術團隊
// FAQ

常見問題

Laravel 的 Middleware 和 Validation 有什麼差別?
Middleware(中介層)像大樓保全,負責檢查請求有沒有識別證(Token)、是否被列入黑名單、是否觸及速率限制等通行條件。Validation(驗證)像櫃檯行政人員,在放行後檢查送進來的資料格式是否正確、Email 是否合法、資料是否符合規則。
前端已經做了表單驗證,後端還需要驗證嗎?
絕對需要。前端驗證只是用來提升使用者體驗,無法防駭客。任何人都能透過 Postman 或 cURL 直接對 API 發送請求,繞過所有前端驗證。後端沒做好把關,惡意資料就會直接湧入資料庫。
Laravel 11 的 Middleware 註冊方式和舊版有什麼不同?
Laravel 10 以前在 app/Http/Kernel.php 註冊 Middleware,但 Laravel 11 已移除 Kernel.php。現在改在 bootstrap/app.php 透過 withMiddleware() 設定,例如用 $middleware->alias() 註冊別名後即可在路由中使用。
為什麼建議用 Form Request 而不是在 Controller 裡寫驗證?
把驗證邏輯寫在 Controller 裡的 $request->validate([...]) 會讓 Controller 變得肥大且難維護。改用 php artisan make:request 建立 Form Request 類別,把規則集中在 rules() 方法、權限判斷放在 authorize() 方法,並可在 messages() 自訂錯誤訊息。當 Controller 方法以該 Form Request 作為 Type Hint 時,Laravel 會在進入方法前自動執行驗證。
~/roamer-tech/newsletter // FREE
// newsletter

訂閱免費電子報

把 AI 自動化、企業系統設計與 WordPress / Laravel 開發的真實案例和可直接照做的技巧,整理成電子報寄給你。只寄精選內容、不灌垃圾信,一鍵就能退訂。

$
// final.exec()

準備好讓你的網站開始為你工作了嗎?