拒絕髒資料進門!Laravel 11 終極驗證 (Validation) 與客製化中介層 (Middleware) 實戰攻略

2026/01/1 | Laravel技術分享, 全端與程式開發, 網站安全與防護

Laravel 11 終極防線:拒絕髒資料!

告別義大利麵防線!資深工程師教你在 Laravel 11 中,如何以 Middleware 執行保全任務,用 Form Request 確保資料純淨。後端絕不能裸奔,前端驗證擋不住駭客!立即掌握 Laravel 11 的架構新制,將驗證與權限分離,打造高效且穩固的 API 城堡。別讓技術債拖垮業務,立即聯繫我們,重構出藝術品般的程式碼!

需要專業協助?

聯絡浪花專案團隊 →

拒絕髒資料進門!Laravel 11 終極驗證 (Validation) 與客製化中介層 (Middleware) 實戰攻略

嗨大家,我是 Eric,浪花科技 (Roamer Tech) 的資深工程師。今天不聊那些飄在天上的 AI 概念,我們要來聊點「硬核」且每天都會遇到的後端生存法則。

你是否曾經接手過這種專案:打開 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)

Q1: Middleware 和 Validation 的最大差別是什麼?

簡單來說,Middleware 是檢查「你能不能進來」(權限、頻率、IP),而 Validation 是檢查「你帶的東西對不對」(欄位格式、數值範圍)。Middleware 通常在 Controller 之前執行,擋掉不合法的請求者;Validation 則確保進入 Controller 處理邏輯的資料是安全且正確的。

Q2: Laravel 11 的 Middleware 設定為什麼找不到 Kernel.php?

這是 Laravel 11 的重大更新之一!為了簡化目錄結構,Laravel 11 移除了 app/Http/Kernel.php,所有的 Middleware 設定(全域、群組、別名)現在都統一在 bootstrap/app.php 檔案中使用 withMiddleware 方法來配置。

Q3: 為什麼不建議在 Controller 裡面直接寫 $request->validate()?

雖然這樣寫很快,但違反了單一職責原則 (SRP)。Controller 應該只負責調度邏輯,不該包含繁雜的驗證規則。將驗證邏輯移到 Form Request 類別中,不僅可以讓 Controller 更乾淨,還能讓驗證規則在不同的請求中被重複利用。

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